rustfmt (#151)
This commit is contained in:
parent
da558c7ce7
commit
899cc32e45
|
@ -1,32 +1,31 @@
|
|||
use std::env;
|
||||
use std::process;
|
||||
|
||||
|
||||
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
|
||||
// runs this build process.
|
||||
let cargo_bin = env::var("CARGO").expect("CARGO env variable should be defined");
|
||||
// The CARGO environment variable provides a path to the executable that
|
||||
// runs this build process.
|
||||
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
|
||||
// will be used in benchmarks.
|
||||
let output = process::Command::new(cargo_bin)
|
||||
.arg("build")
|
||||
.arg("--target=wasm32-unknown-unknown")
|
||||
.arg("--release")
|
||||
.arg("--manifest-path=./wasm-kernel/Cargo.toml")
|
||||
.arg("--verbose")
|
||||
.output()
|
||||
.expect("failed to execute `cargo`");
|
||||
// Build a release version of wasm-kernel. The code in the output wasm binary
|
||||
// will be used in benchmarks.
|
||||
let output = process::Command::new(cargo_bin)
|
||||
.arg("build")
|
||||
.arg("--target=wasm32-unknown-unknown")
|
||||
.arg("--release")
|
||||
.arg("--manifest-path=./wasm-kernel/Cargo.toml")
|
||||
.arg("--verbose")
|
||||
.output()
|
||||
.expect("failed to execute `cargo`");
|
||||
|
||||
if !output.status.success() {
|
||||
let msg = format!(
|
||||
"status: {status}\nstdout: {stdout}\nstderr: {stderr}\n",
|
||||
status=output.status,
|
||||
stdout=String::from_utf8_lossy(&output.stdout),
|
||||
stderr=String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
panic!("{}", msg);
|
||||
}
|
||||
if !output.status.success() {
|
||||
let msg = format!(
|
||||
"status: {status}\nstdout: {stdout}\nstderr: {stderr}\n",
|
||||
status = output.status,
|
||||
stdout = String::from_utf8_lossy(&output.stdout),
|
||||
stderr = String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
panic!("{}", msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
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 {
|
||||
use std::io::prelude::*;
|
||||
let mut file = File::open(filename).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
Module::from_buffer(buf).unwrap()
|
||||
use std::io::prelude::*;
|
||||
let mut file = File::open(filename).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
Module::from_buffer(buf).unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -27,10 +27,10 @@ fn main() {
|
|||
let module = load_from_file(&args[1]);
|
||||
|
||||
// Intialize deserialized module. It adds module into It expects 3 parameters:
|
||||
// - a name for the module
|
||||
// - a module declaration
|
||||
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
|
||||
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
|
||||
// - a name for the module
|
||||
// - a module declaration
|
||||
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
|
||||
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
|
||||
let main = ModuleInstance::new(&module, &ImportsBuilder::default())
|
||||
.expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
|
@ -40,5 +40,8 @@ fn main() {
|
|||
let argument: i32 = args[2].parse().expect("Integer argument required");
|
||||
|
||||
// "_call" export of function to be executed with an i32 argument and prints the result of execution
|
||||
println!("Result: {:?}", main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals));
|
||||
println!(
|
||||
"Result: {:?}",
|
||||
main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,9 +3,8 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
|
||||
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType};
|
||||
use wasmi::{RuntimeValue, ModuleInstance, NopExternals, ImportsBuilder};
|
||||
|
||||
use parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType};
|
||||
use wasmi::{ImportsBuilder, ModuleInstance, NopExternals, RuntimeValue};
|
||||
|
||||
fn main() {
|
||||
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
|
||||
let export_section = module.export_section().expect("No export section found");
|
||||
// It's a section with function declarations (which are references to the type section entries)
|
||||
let function_section = module.function_section().expect("No function section found");
|
||||
let function_section = module
|
||||
.function_section()
|
||||
.expect("No function section found");
|
||||
// Type section stores function types which are referenced by function_section entries
|
||||
let type_section = module.type_section().expect("No type section found");
|
||||
|
||||
// Given function name used to find export section entry which contains
|
||||
// an `internal` field which points to the index in the function index space
|
||||
let found_entry = export_section.entries().iter()
|
||||
.find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name));
|
||||
let found_entry = export_section
|
||||
.entries()
|
||||
.iter()
|
||||
.find(|entry| func_name == entry.field())
|
||||
.expect(&format!("No export with name {} found", func_name));
|
||||
|
||||
// Function index in the function index space (internally-defined + imported)
|
||||
let function_index: usize = match found_entry.internal() {
|
||||
|
@ -41,11 +45,14 @@ fn main() {
|
|||
// We need to count import section entries (functions only!) to subtract it from function_index
|
||||
// and obtain the index within the function section
|
||||
let import_section_len: usize = match module.import_section() {
|
||||
Some(import) =>
|
||||
import.entries().iter().filter(|entry| match entry.external() {
|
||||
Some(import) => import
|
||||
.entries()
|
||||
.iter()
|
||||
.filter(|entry| match entry.external() {
|
||||
&External::Function(_) => true,
|
||||
_ => false,
|
||||
}).count(),
|
||||
})
|
||||
.count(),
|
||||
None => 0,
|
||||
};
|
||||
|
||||
|
@ -53,7 +60,8 @@ fn main() {
|
|||
let function_index_in_section = function_index - import_section_len;
|
||||
|
||||
// Getting a type reference from a function section entry
|
||||
let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize;
|
||||
let func_type_ref: usize =
|
||||
function_section.entries()[function_index_in_section].type_ref() as usize;
|
||||
|
||||
// Use the reference to get an actual function type
|
||||
let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
|
||||
|
@ -61,12 +69,35 @@ fn main() {
|
|||
};
|
||||
|
||||
// Parses arguments and constructs runtime values in correspondence of their types
|
||||
function_type.params().iter().enumerate().map(|(i, value)| match value {
|
||||
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
|
||||
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
|
||||
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i])).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>>()
|
||||
function_type
|
||||
.params()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, value)| match value {
|
||||
&ValueType::I32 => RuntimeValue::I32(
|
||||
program_args[i]
|
||||
.parse::<i32>()
|
||||
.expect(&format!("Can't parse arg #{} as i32", program_args[i])),
|
||||
),
|
||||
&ValueType::I64 => RuntimeValue::I64(
|
||||
program_args[i]
|
||||
.parse::<i64>()
|
||||
.expect(&format!("Can't parse arg #{} as i64", program_args[i])),
|
||||
),
|
||||
&ValueType::F32 => RuntimeValue::F32(
|
||||
program_args[i]
|
||||
.parse::<f32>()
|
||||
.expect(&format!("Can't parse arg #{} as f32", program_args[i]))
|
||||
.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");
|
||||
|
@ -81,5 +112,9 @@ fn main() {
|
|||
.run_start(&mut NopExternals)
|
||||
.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("")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,256 +1,255 @@
|
|||
extern crate wasmi;
|
||||
extern crate parity_wasm;
|
||||
extern crate wasmi;
|
||||
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use wasmi::{
|
||||
Error as InterpreterError, ModuleInstance, ModuleRef,
|
||||
Externals, RuntimeValue, FuncRef, ModuleImportResolver,
|
||||
FuncInstance, HostError, ImportsBuilder, Signature, ValueType,
|
||||
RuntimeArgs, Trap,
|
||||
Error as InterpreterError, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
|
||||
ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, Trap,
|
||||
ValueType,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
OutOfRange,
|
||||
AlreadyOccupied,
|
||||
Interpreter(InterpreterError),
|
||||
OutOfRange,
|
||||
AlreadyOccupied,
|
||||
Interpreter(InterpreterError),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InterpreterError> for Error {
|
||||
fn from(e: InterpreterError) -> Self {
|
||||
Error::Interpreter(e)
|
||||
}
|
||||
fn from(e: InterpreterError) -> Self {
|
||||
Error::Interpreter(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl HostError for Error {}
|
||||
|
||||
mod tictactoe {
|
||||
use super::Error;
|
||||
use super::Error;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Player {
|
||||
X,
|
||||
O,
|
||||
}
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Player {
|
||||
X,
|
||||
O,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum GameResult {
|
||||
Draw,
|
||||
Won(Player),
|
||||
}
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum GameResult {
|
||||
Draw,
|
||||
Won(Player),
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn into_i32(maybe_player: Option<Player>) -> i32 {
|
||||
match maybe_player {
|
||||
None => 0,
|
||||
Some(Player::X) => 1,
|
||||
Some(Player::O) => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Player {
|
||||
pub fn into_i32(maybe_player: Option<Player>) -> i32 {
|
||||
match maybe_player {
|
||||
None => 0,
|
||||
Some(Player::X) => 1,
|
||||
Some(Player::O) => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Game {
|
||||
board: [Option<Player>; 9],
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct Game {
|
||||
board: [Option<Player>; 9],
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new() -> Game {
|
||||
Game {
|
||||
board: [None; 9],
|
||||
}
|
||||
}
|
||||
impl Game {
|
||||
pub fn new() -> Game {
|
||||
Game { board: [None; 9] }
|
||||
}
|
||||
|
||||
pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
|
||||
if idx < 0 || idx > 9 {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
if self.board[idx as usize] != None {
|
||||
return Err(Error::AlreadyOccupied);
|
||||
}
|
||||
self.board[idx as usize] = Some(player);
|
||||
Ok(())
|
||||
}
|
||||
pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
|
||||
if idx < 0 || idx > 9 {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
if self.board[idx as usize] != None {
|
||||
return Err(Error::AlreadyOccupied);
|
||||
}
|
||||
self.board[idx as usize] = Some(player);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, idx: i32) -> Result<Option<Player>, Error> {
|
||||
if idx < 0 || idx > 9 {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
Ok(self.board[idx as usize])
|
||||
}
|
||||
pub fn get(&self, idx: i32) -> Result<Option<Player>, Error> {
|
||||
if idx < 0 || idx > 9 {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
Ok(self.board[idx as usize])
|
||||
}
|
||||
|
||||
pub fn game_result(&self) -> Option<GameResult> {
|
||||
// 0, 1, 2
|
||||
// 3, 4, 5
|
||||
// 6, 7, 8
|
||||
let patterns = &[
|
||||
// Rows
|
||||
(0, 1, 2),
|
||||
(3, 4, 5),
|
||||
(6, 7, 8),
|
||||
pub fn game_result(&self) -> Option<GameResult> {
|
||||
// 0, 1, 2
|
||||
// 3, 4, 5
|
||||
// 6, 7, 8
|
||||
let patterns = &[
|
||||
// Rows
|
||||
(0, 1, 2),
|
||||
(3, 4, 5),
|
||||
(6, 7, 8),
|
||||
// Columns
|
||||
(0, 3, 6),
|
||||
(1, 4, 7),
|
||||
(2, 5, 8),
|
||||
// Diagonals
|
||||
(0, 4, 8),
|
||||
(2, 4, 6),
|
||||
];
|
||||
|
||||
// Columns
|
||||
(0, 3, 6),
|
||||
(1, 4, 7),
|
||||
(2, 5, 8),
|
||||
// Returns Some(player) if all cells contain same Player.
|
||||
let all_same = |i1: usize, i2: usize, i3: usize| -> Option<Player> {
|
||||
if self.board[i1].is_none() {
|
||||
return None;
|
||||
}
|
||||
if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] {
|
||||
return self.board[i1];
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
// Diagonals
|
||||
(0, 4, 8),
|
||||
(2, 4, 6),
|
||||
];
|
||||
for &(i1, i2, i3) in patterns {
|
||||
if let Some(player) = all_same(i1, i2, i3) {
|
||||
return Some(GameResult::Won(player));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns Some(player) if all cells contain same Player.
|
||||
let all_same = |i1: usize, i2: usize, i3: usize| -> Option<Player> {
|
||||
if self.board[i1].is_none() {
|
||||
return None;
|
||||
}
|
||||
if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] {
|
||||
return self.board[i1];
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
for &(i1, i2, i3) in patterns {
|
||||
if let Some(player) = all_same(i1, i2, i3) {
|
||||
return Some(GameResult::Won(player));
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, there is no winner. Check if it's draw.
|
||||
let all_occupied = self.board.iter().all(|&cell| cell.is_some());
|
||||
if all_occupied {
|
||||
Some(GameResult::Draw)
|
||||
} else {
|
||||
// Nah, there are still empty cells left.
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ok, there is no winner. Check if it's draw.
|
||||
let all_occupied = self.board.iter().all(|&cell| cell.is_some());
|
||||
if all_occupied {
|
||||
Some(GameResult::Draw)
|
||||
} else {
|
||||
// Nah, there are still empty cells left.
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Runtime<'a> {
|
||||
player: tictactoe::Player,
|
||||
game: &'a mut tictactoe::Game,
|
||||
player: tictactoe::Player,
|
||||
game: &'a mut tictactoe::Game,
|
||||
}
|
||||
|
||||
const SET_FUNC_INDEX: usize = 0;
|
||||
const GET_FUNC_INDEX: usize = 1;
|
||||
|
||||
impl<'a> Externals for Runtime<'a> {
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
match index {
|
||||
SET_FUNC_INDEX => {
|
||||
let idx: i32 = args.nth(0);
|
||||
self.game.set(idx, self.player)?;
|
||||
Ok(None)
|
||||
}
|
||||
GET_FUNC_INDEX => {
|
||||
let idx: i32 = args.nth(0);
|
||||
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
|
||||
Ok(Some(val.into()))
|
||||
}
|
||||
_ => panic!("unknown function index")
|
||||
}
|
||||
}
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
match index {
|
||||
SET_FUNC_INDEX => {
|
||||
let idx: i32 = args.nth(0);
|
||||
self.game.set(idx, self.player)?;
|
||||
Ok(None)
|
||||
}
|
||||
GET_FUNC_INDEX => {
|
||||
let idx: i32 = args.nth(0);
|
||||
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
|
||||
Ok(Some(val.into()))
|
||||
}
|
||||
_ => panic!("unknown function index"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RuntimeModuleImportResolver;
|
||||
|
||||
impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_signature: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
let func_ref = match field_name {
|
||||
"set" => {
|
||||
FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], None), SET_FUNC_INDEX)
|
||||
},
|
||||
"get" => FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], Some(ValueType::I32)), GET_FUNC_INDEX),
|
||||
_ => return Err(
|
||||
InterpreterError::Function(
|
||||
format!("host module doesn't export function with name {}", field_name)
|
||||
)
|
||||
)
|
||||
};
|
||||
Ok(func_ref)
|
||||
}
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_signature: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
let func_ref = match field_name {
|
||||
"set" => FuncInstance::alloc_host(
|
||||
Signature::new(&[ValueType::I32][..], None),
|
||||
SET_FUNC_INDEX,
|
||||
),
|
||||
"get" => FuncInstance::alloc_host(
|
||||
Signature::new(&[ValueType::I32][..], Some(ValueType::I32)),
|
||||
GET_FUNC_INDEX,
|
||||
),
|
||||
_ => {
|
||||
return Err(InterpreterError::Function(format!(
|
||||
"host module doesn't export function with name {}",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
};
|
||||
Ok(func_ref)
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate(path: &str) -> Result<ModuleRef, Error> {
|
||||
let module = {
|
||||
use std::io::prelude::*;
|
||||
let mut file = File::open(path).unwrap();
|
||||
let mut wasm_buf = Vec::new();
|
||||
file.read_to_end(&mut wasm_buf).unwrap();
|
||||
wasmi::Module::from_buffer(&wasm_buf)?
|
||||
};
|
||||
let module = {
|
||||
use std::io::prelude::*;
|
||||
let mut file = File::open(path).unwrap();
|
||||
let mut wasm_buf = Vec::new();
|
||||
file.read_to_end(&mut wasm_buf).unwrap();
|
||||
wasmi::Module::from_buffer(&wasm_buf)?
|
||||
};
|
||||
|
||||
let mut imports = ImportsBuilder::new();
|
||||
imports.push_resolver("env", &RuntimeModuleImportResolver);
|
||||
let mut imports = ImportsBuilder::new();
|
||||
imports.push_resolver("env", &RuntimeModuleImportResolver);
|
||||
|
||||
let instance = ModuleInstance::new(&module, &imports)?
|
||||
.assert_no_start();
|
||||
let instance = ModuleInstance::new(&module, &imports)?.assert_no_start();
|
||||
|
||||
Ok(instance)
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
fn play(
|
||||
x_instance: ModuleRef,
|
||||
o_instance: ModuleRef,
|
||||
game: &mut tictactoe::Game,
|
||||
x_instance: ModuleRef,
|
||||
o_instance: ModuleRef,
|
||||
game: &mut tictactoe::Game,
|
||||
) -> Result<tictactoe::GameResult, Error> {
|
||||
let mut turn_of = tictactoe::Player::X;
|
||||
let game_result = loop {
|
||||
let (instance, next_turn_of) = match turn_of {
|
||||
tictactoe::Player::X => (&x_instance, tictactoe::Player::O),
|
||||
tictactoe::Player::O => (&o_instance, tictactoe::Player::X),
|
||||
};
|
||||
let mut turn_of = tictactoe::Player::X;
|
||||
let game_result = loop {
|
||||
let (instance, next_turn_of) = match turn_of {
|
||||
tictactoe::Player::X => (&x_instance, tictactoe::Player::O),
|
||||
tictactoe::Player::O => (&o_instance, tictactoe::Player::X),
|
||||
};
|
||||
|
||||
{
|
||||
let mut runtime = Runtime {
|
||||
player: turn_of,
|
||||
game: game,
|
||||
};
|
||||
let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?;
|
||||
}
|
||||
{
|
||||
let mut runtime = Runtime {
|
||||
player: turn_of,
|
||||
game: game,
|
||||
};
|
||||
let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?;
|
||||
}
|
||||
|
||||
match game.game_result() {
|
||||
Some(game_result) => break game_result,
|
||||
None => {}
|
||||
}
|
||||
match game.game_result() {
|
||||
Some(game_result) => break game_result,
|
||||
None => {}
|
||||
}
|
||||
|
||||
turn_of = next_turn_of;
|
||||
};
|
||||
turn_of = next_turn_of;
|
||||
};
|
||||
|
||||
Ok(game_result)
|
||||
Ok(game_result)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut game = tictactoe::Game::new();
|
||||
let mut game = tictactoe::Game::new();
|
||||
|
||||
let args: Vec<_> = env::args().collect();
|
||||
if args.len() < 3 {
|
||||
println!("Usage: {} <x player module> <y player module>", args[0]);
|
||||
return;
|
||||
}
|
||||
let args: Vec<_> = env::args().collect();
|
||||
if args.len() < 3 {
|
||||
println!("Usage: {} <x player module> <y player module>", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Instantiate modules of X and O players.
|
||||
let x_instance = instantiate(&args[1]).expect("X player module to load");
|
||||
let o_instance = instantiate(&args[2]).expect("Y player module to load");
|
||||
// Instantiate modules of X and O players.
|
||||
let x_instance = instantiate(&args[1]).expect("X player module to load");
|
||||
let o_instance = instantiate(&args[2]).expect("Y player module to load");
|
||||
|
||||
let result = play(x_instance, o_instance, &mut game);
|
||||
println!("result = {:?}, game = {:#?}", result, game);
|
||||
let result = play(x_instance, o_instance, &mut game);
|
||||
println!("result = {:?}, game = {:#?}", result, game);
|
||||
}
|
||||
|
|
|
@ -5,77 +5,79 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
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::{
|
||||
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 {
|
||||
use std::io::prelude::*;
|
||||
let mut file = File::open(filename).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
Module::from_buffer(buf).unwrap()
|
||||
use std::io::prelude::*;
|
||||
let mut file = File::open(filename).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
Module::from_buffer(buf).unwrap()
|
||||
}
|
||||
|
||||
struct ResolveAll;
|
||||
|
||||
impl ModuleImportResolver for ResolveAll {
|
||||
fn resolve_func(&self, _field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
Ok(FuncInstance::alloc_host(signature.clone(), 0))
|
||||
}
|
||||
fn resolve_func(&self, _field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
Ok(FuncInstance::alloc_host(signature.clone(), 0))
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
&self,
|
||||
_field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
Ok(GlobalInstance::alloc(
|
||||
RuntimeValue::default(global_type.value_type()),
|
||||
global_type.is_mutable(),
|
||||
))
|
||||
}
|
||||
fn resolve_global(
|
||||
&self,
|
||||
_field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
Ok(GlobalInstance::alloc(
|
||||
RuntimeValue::default(global_type.value_type()),
|
||||
global_type.is_mutable(),
|
||||
))
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
_field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Ok(MemoryInstance::alloc(
|
||||
Pages(memory_type.initial() as usize),
|
||||
memory_type.maximum().map(|m| Pages(m as usize)),
|
||||
).unwrap())
|
||||
}
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
_field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Ok(MemoryInstance::alloc(
|
||||
Pages(memory_type.initial() as usize),
|
||||
memory_type.maximum().map(|m| Pages(m as usize)),
|
||||
)
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
&self,
|
||||
_field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
Ok(TableInstance::alloc(table_type.initial(), table_type.maximum()).unwrap())
|
||||
}
|
||||
fn resolve_table(
|
||||
&self,
|
||||
_field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
Ok(TableInstance::alloc(table_type.initial(), table_type.maximum()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<_> = args().collect();
|
||||
if args.len() != 2 {
|
||||
println!("Usage: {} <wasm file>", args[0]);
|
||||
return;
|
||||
}
|
||||
let module = load_from_file(&args[1]);
|
||||
let _ = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::default()
|
||||
// Well known imports.
|
||||
.with_resolver("env", &ResolveAll)
|
||||
.with_resolver("global", &ResolveAll)
|
||||
.with_resolver("foo", &ResolveAll)
|
||||
.with_resolver("global.Math", &ResolveAll)
|
||||
.with_resolver("asm2wasm", &ResolveAll)
|
||||
.with_resolver("spectest", &ResolveAll),
|
||||
).expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
let args: Vec<_> = args().collect();
|
||||
if args.len() != 2 {
|
||||
println!("Usage: {} <wasm file>", args[0]);
|
||||
return;
|
||||
}
|
||||
let module = load_from_file(&args[1]);
|
||||
let _ = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::default()
|
||||
// Well known imports.
|
||||
.with_resolver("env", &ResolveAll)
|
||||
.with_resolver("global", &ResolveAll)
|
||||
.with_resolver("foo", &ResolveAll)
|
||||
.with_resolver("global.Math", &ResolveAll)
|
||||
.with_resolver("asm2wasm", &ResolveAll)
|
||||
.with_resolver("spectest", &ResolveAll),
|
||||
)
|
||||
.expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
pub mod stack;
|
||||
|
||||
/// Index of default linear memory.
|
||||
|
|
|
@ -1,88 +1,101 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
||||
use core::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error(String);
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
fn description(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Stack with limit.
|
||||
#[derive(Debug)]
|
||||
pub struct StackWithLimit<T> where T: Clone {
|
||||
/// Stack values.
|
||||
values: Vec<T>,
|
||||
/// Stack limit (maximal stack len).
|
||||
limit: usize,
|
||||
pub struct StackWithLimit<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
/// Stack values.
|
||||
values: Vec<T>,
|
||||
/// Stack limit (maximal stack len).
|
||||
limit: usize,
|
||||
}
|
||||
|
||||
impl<T> StackWithLimit<T> where T: Clone {
|
||||
pub fn with_limit(limit: usize) -> Self {
|
||||
StackWithLimit {
|
||||
values: Vec::new(),
|
||||
limit: limit
|
||||
}
|
||||
}
|
||||
impl<T> StackWithLimit<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
pub fn with_limit(limit: usize) -> Self {
|
||||
StackWithLimit {
|
||||
values: Vec::new(),
|
||||
limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.is_empty()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
pub fn top(&self) -> Result<&T, Error> {
|
||||
self.values
|
||||
.last()
|
||||
.ok_or_else(|| Error("non-empty stack expected".into()))
|
||||
}
|
||||
pub fn top(&self) -> Result<&T, Error> {
|
||||
self.values
|
||||
.last()
|
||||
.ok_or_else(|| Error("non-empty stack expected".into()))
|
||||
}
|
||||
|
||||
pub fn top_mut(&mut self) -> Result<&mut T, Error> {
|
||||
self.values
|
||||
.last_mut()
|
||||
.ok_or_else(|| Error("non-empty stack expected".into()))
|
||||
}
|
||||
pub fn top_mut(&mut self) -> Result<&mut T, Error> {
|
||||
self.values
|
||||
.last_mut()
|
||||
.ok_or_else(|| Error("non-empty stack expected".into()))
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> Result<&T, Error> {
|
||||
if index >= self.values.len() {
|
||||
return Err(Error(format!("trying to get value at position {} on stack of size {}", index, self.values.len())));
|
||||
}
|
||||
pub fn get(&self, index: usize) -> Result<&T, Error> {
|
||||
if 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> {
|
||||
if self.values.len() >= self.limit {
|
||||
return Err(Error(format!("exceeded stack limit {}", self.limit)));
|
||||
}
|
||||
pub fn push(&mut self, value: T) -> Result<(), Error> {
|
||||
if self.values.len() >= self.limit {
|
||||
return Err(Error(format!("exceeded stack limit {}", self.limit)));
|
||||
}
|
||||
|
||||
self.values.push(value);
|
||||
Ok(())
|
||||
}
|
||||
self.values.push(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Result<T, Error> {
|
||||
self.values
|
||||
.pop()
|
||||
.ok_or_else(|| Error("non-empty stack expected".into()))
|
||||
}
|
||||
pub fn pop(&mut self) -> Result<T, Error> {
|
||||
self.values
|
||||
.pop()
|
||||
.ok_or_else(|| Error("non-empty stack expected".into()))
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: usize, dummy: T) {
|
||||
debug_assert!(new_size <= self.values.len());
|
||||
self.values.resize(new_size, dummy);
|
||||
}
|
||||
pub fn resize(&mut self, new_size: usize, dummy: T) {
|
||||
debug_assert!(new_size <= self.values.len());
|
||||
self.values.resize(new_size, dummy);
|
||||
}
|
||||
}
|
||||
|
|
480
src/func.rs
480
src/func.rs
|
@ -2,14 +2,14 @@
|
|||
use alloc::prelude::*;
|
||||
use alloc::rc::{Rc, Weak};
|
||||
use core::fmt;
|
||||
use parity_wasm::elements::Local;
|
||||
use {Trap, Signature};
|
||||
use host::Externals;
|
||||
use runner::{check_function_args, Interpreter, InterpreterState};
|
||||
use value::RuntimeValue;
|
||||
use types::ValueType;
|
||||
use module::ModuleInstance;
|
||||
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).
|
||||
///
|
||||
|
@ -20,10 +20,10 @@ use isa;
|
|||
pub struct FuncRef(Rc<FuncInstance>);
|
||||
|
||||
impl ::core::ops::Deref for FuncRef {
|
||||
type Target = FuncInstance;
|
||||
fn deref(&self) -> &FuncInstance {
|
||||
&self.0
|
||||
}
|
||||
type Target = FuncInstance;
|
||||
fn deref(&self) -> &FuncInstance {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime representation of a function.
|
||||
|
@ -44,271 +44,271 @@ pub struct FuncInstance(FuncInstanceInternal);
|
|||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum FuncInstanceInternal {
|
||||
Internal {
|
||||
signature: Rc<Signature>,
|
||||
module: Weak<ModuleInstance>,
|
||||
body: Rc<FuncBody>,
|
||||
},
|
||||
Host {
|
||||
signature: Signature,
|
||||
host_func_index: usize,
|
||||
},
|
||||
Internal {
|
||||
signature: Rc<Signature>,
|
||||
module: Weak<ModuleInstance>,
|
||||
body: Rc<FuncBody>,
|
||||
},
|
||||
Host {
|
||||
signature: Signature,
|
||||
host_func_index: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Debug for FuncInstance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.as_internal() {
|
||||
&FuncInstanceInternal::Internal {
|
||||
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,
|
||||
)
|
||||
}
|
||||
&FuncInstanceInternal::Host { ref signature, .. } => {
|
||||
write!(f, "Host {{ signature={:?} }}", signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.as_internal() {
|
||||
&FuncInstanceInternal::Internal { 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,)
|
||||
}
|
||||
&FuncInstanceInternal::Host { ref signature, .. } => {
|
||||
write!(f, "Host {{ signature={:?} }}", signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FuncInstance {
|
||||
/// Allocate a function instance for a host function.
|
||||
///
|
||||
/// When this function instance will be called by the wasm code,
|
||||
/// the instance of [`Externals`] will be invoked by calling `invoke_index`
|
||||
/// with specified `host_func_index` here.
|
||||
/// This call will be made with the `signature` provided here.
|
||||
///
|
||||
/// [`Externals`]: trait.Externals.html
|
||||
pub fn alloc_host(signature: Signature, host_func_index: usize) -> FuncRef {
|
||||
let func = FuncInstanceInternal::Host {
|
||||
signature,
|
||||
host_func_index,
|
||||
};
|
||||
FuncRef(Rc::new(FuncInstance(func)))
|
||||
}
|
||||
/// Allocate a function instance for a host function.
|
||||
///
|
||||
/// When this function instance will be called by the wasm code,
|
||||
/// the instance of [`Externals`] will be invoked by calling `invoke_index`
|
||||
/// with specified `host_func_index` here.
|
||||
/// This call will be made with the `signature` provided here.
|
||||
///
|
||||
/// [`Externals`]: trait.Externals.html
|
||||
pub fn alloc_host(signature: Signature, host_func_index: usize) -> FuncRef {
|
||||
let func = FuncInstanceInternal::Host {
|
||||
signature,
|
||||
host_func_index,
|
||||
};
|
||||
FuncRef(Rc::new(FuncInstance(func)))
|
||||
}
|
||||
|
||||
/// Returns [signature] of this function instance.
|
||||
///
|
||||
/// This function instance can only be called with matching signatures.
|
||||
///
|
||||
/// [signature]: struct.Signature.html
|
||||
pub fn signature(&self) -> &Signature {
|
||||
match *self.as_internal() {
|
||||
FuncInstanceInternal::Internal { ref signature, .. } => signature,
|
||||
FuncInstanceInternal::Host { ref signature, .. } => signature,
|
||||
}
|
||||
}
|
||||
/// Returns [signature] of this function instance.
|
||||
///
|
||||
/// This function instance can only be called with matching signatures.
|
||||
///
|
||||
/// [signature]: struct.Signature.html
|
||||
pub fn signature(&self) -> &Signature {
|
||||
match *self.as_internal() {
|
||||
FuncInstanceInternal::Internal { ref signature, .. } => signature,
|
||||
FuncInstanceInternal::Host { ref signature, .. } => signature,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_internal(&self) -> &FuncInstanceInternal {
|
||||
&self.0
|
||||
}
|
||||
pub(crate) fn as_internal(&self) -> &FuncInstanceInternal {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_internal(
|
||||
module: Weak<ModuleInstance>,
|
||||
signature: Rc<Signature>,
|
||||
body: FuncBody,
|
||||
) -> FuncRef {
|
||||
let func = FuncInstanceInternal::Internal {
|
||||
signature,
|
||||
module: module,
|
||||
body: Rc::new(body),
|
||||
};
|
||||
FuncRef(Rc::new(FuncInstance(func)))
|
||||
}
|
||||
pub(crate) fn alloc_internal(
|
||||
module: Weak<ModuleInstance>,
|
||||
signature: Rc<Signature>,
|
||||
body: FuncBody,
|
||||
) -> FuncRef {
|
||||
let func = FuncInstanceInternal::Internal {
|
||||
signature,
|
||||
module: module,
|
||||
body: Rc::new(body),
|
||||
};
|
||||
FuncRef(Rc::new(FuncInstance(func)))
|
||||
}
|
||||
|
||||
pub(crate) fn body(&self) -> Option<Rc<FuncBody>> {
|
||||
match *self.as_internal() {
|
||||
FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)),
|
||||
FuncInstanceInternal::Host { .. } => None,
|
||||
}
|
||||
}
|
||||
pub(crate) fn body(&self) -> Option<Rc<FuncBody>> {
|
||||
match *self.as_internal() {
|
||||
FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)),
|
||||
FuncInstanceInternal::Host { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke this function.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if `args` types is not match function [`signature`] or
|
||||
/// if [`Trap`] at execution time occured.
|
||||
///
|
||||
/// [`signature`]: #method.signature
|
||||
/// [`Trap`]: #enum.Trap.html
|
||||
pub fn invoke<E: Externals>(
|
||||
func: &FuncRef,
|
||||
args: &[RuntimeValue],
|
||||
externals: &mut E,
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
check_function_args(func.signature(), &args)?;
|
||||
match *func.as_internal() {
|
||||
FuncInstanceInternal::Internal { .. } => {
|
||||
let mut interpreter = Interpreter::new(func, args)?;
|
||||
interpreter.start_execution(externals)
|
||||
}
|
||||
FuncInstanceInternal::Host {
|
||||
ref host_func_index,
|
||||
..
|
||||
} => externals.invoke_index(*host_func_index, args.into()),
|
||||
}
|
||||
}
|
||||
/// Invoke this function.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if `args` types is not match function [`signature`] or
|
||||
/// if [`Trap`] at execution time occured.
|
||||
///
|
||||
/// [`signature`]: #method.signature
|
||||
/// [`Trap`]: #enum.Trap.html
|
||||
pub fn invoke<E: Externals>(
|
||||
func: &FuncRef,
|
||||
args: &[RuntimeValue],
|
||||
externals: &mut E,
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
check_function_args(func.signature(), &args)?;
|
||||
match *func.as_internal() {
|
||||
FuncInstanceInternal::Internal { .. } => {
|
||||
let mut interpreter = Interpreter::new(func, args)?;
|
||||
interpreter.start_execution(externals)
|
||||
}
|
||||
FuncInstanceInternal::Host {
|
||||
ref host_func_index,
|
||||
..
|
||||
} => 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
|
||||
/// Host trap happens, caller can use [`resume_execution`] to feed the expected return value back in, and then
|
||||
/// continue the execution.
|
||||
///
|
||||
/// This is an experimental API, and this functionality may not be available in other WebAssembly engines.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if `args` types is not match function [`signature`].
|
||||
///
|
||||
/// [`signature`]: #method.signature
|
||||
/// [`Trap`]: #enum.Trap.html
|
||||
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
|
||||
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
|
||||
pub fn invoke_resumable<'args>(
|
||||
func: &FuncRef,
|
||||
args: &'args [RuntimeValue],
|
||||
) -> Result<FuncInvocation<'args>, Trap> {
|
||||
check_function_args(func.signature(), &args)?;
|
||||
match *func.as_internal() {
|
||||
FuncInstanceInternal::Internal { .. } => {
|
||||
let interpreter = Interpreter::new(func, args)?;
|
||||
Ok(FuncInvocation {
|
||||
kind: FuncInvocationKind::Internal(interpreter),
|
||||
})
|
||||
}
|
||||
FuncInstanceInternal::Host {
|
||||
ref host_func_index,
|
||||
..
|
||||
} => {
|
||||
Ok(FuncInvocation {
|
||||
kind: FuncInvocationKind::Host {
|
||||
args,
|
||||
host_func_index: *host_func_index,
|
||||
finished: false,
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
/// 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
|
||||
/// continue the execution.
|
||||
///
|
||||
/// This is an experimental API, and this functionality may not be available in other WebAssembly engines.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if `args` types is not match function [`signature`].
|
||||
///
|
||||
/// [`signature`]: #method.signature
|
||||
/// [`Trap`]: #enum.Trap.html
|
||||
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
|
||||
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
|
||||
pub fn invoke_resumable<'args>(
|
||||
func: &FuncRef,
|
||||
args: &'args [RuntimeValue],
|
||||
) -> Result<FuncInvocation<'args>, Trap> {
|
||||
check_function_args(func.signature(), &args)?;
|
||||
match *func.as_internal() {
|
||||
FuncInstanceInternal::Internal { .. } => {
|
||||
let interpreter = Interpreter::new(func, args)?;
|
||||
Ok(FuncInvocation {
|
||||
kind: FuncInvocationKind::Internal(interpreter),
|
||||
})
|
||||
}
|
||||
FuncInstanceInternal::Host {
|
||||
ref host_func_index,
|
||||
..
|
||||
} => Ok(FuncInvocation {
|
||||
kind: FuncInvocationKind::Host {
|
||||
args,
|
||||
host_func_index: *host_func_index,
|
||||
finished: false,
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A resumable invocation error.
|
||||
#[derive(Debug)]
|
||||
pub enum ResumableError {
|
||||
/// Trap happened.
|
||||
Trap(Trap),
|
||||
/// 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:
|
||||
/// - The invocation is directly a host function.
|
||||
/// - The invocation has not been started.
|
||||
/// - The invocation returns normally or returns any trap other than `Host` kind.
|
||||
///
|
||||
/// This error is returned by [`resume_execution`].
|
||||
///
|
||||
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
|
||||
NotResumable,
|
||||
/// The invocation has already been started.
|
||||
///
|
||||
/// This error is returned by [`start_execution`].
|
||||
///
|
||||
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
|
||||
AlreadyStarted,
|
||||
/// Trap happened.
|
||||
Trap(Trap),
|
||||
/// 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:
|
||||
/// - The invocation is directly a host function.
|
||||
/// - The invocation has not been started.
|
||||
/// - The invocation returns normally or returns any trap other than `Host` kind.
|
||||
///
|
||||
/// This error is returned by [`resume_execution`].
|
||||
///
|
||||
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
|
||||
NotResumable,
|
||||
/// The invocation has already been started.
|
||||
///
|
||||
/// This error is returned by [`start_execution`].
|
||||
///
|
||||
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
|
||||
AlreadyStarted,
|
||||
}
|
||||
|
||||
impl From<Trap> for ResumableError {
|
||||
fn from(trap: Trap) -> Self {
|
||||
ResumableError::Trap(trap)
|
||||
}
|
||||
fn from(trap: Trap) -> Self {
|
||||
ResumableError::Trap(trap)
|
||||
}
|
||||
}
|
||||
|
||||
/// A resumable invocation handle. This struct is returned by `FuncInstance::invoke_resumable`.
|
||||
pub struct FuncInvocation<'args> {
|
||||
kind: FuncInvocationKind<'args>,
|
||||
kind: FuncInvocationKind<'args>,
|
||||
}
|
||||
|
||||
enum FuncInvocationKind<'args> {
|
||||
Internal(Interpreter),
|
||||
Host {
|
||||
args: &'args [RuntimeValue],
|
||||
host_func_index: usize,
|
||||
finished: bool
|
||||
},
|
||||
Internal(Interpreter),
|
||||
Host {
|
||||
args: &'args [RuntimeValue],
|
||||
host_func_index: usize,
|
||||
finished: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'args> FuncInvocation<'args> {
|
||||
/// Whether this invocation is currently resumable.
|
||||
pub fn is_resumable(&self) -> bool {
|
||||
match &self.kind {
|
||||
&FuncInvocationKind::Internal(ref interpreter) => interpreter.state().is_resumable(),
|
||||
&FuncInvocationKind::Host { .. } => false,
|
||||
}
|
||||
}
|
||||
/// Whether this invocation is currently resumable.
|
||||
pub fn is_resumable(&self) -> bool {
|
||||
match &self.kind {
|
||||
&FuncInvocationKind::Internal(ref interpreter) => interpreter.state().is_resumable(),
|
||||
&FuncInvocationKind::Host { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If the invocation is resumable, the expected return value type to be feed back in.
|
||||
pub fn resumable_value_type(&self) -> Option<ValueType> {
|
||||
match &self.kind {
|
||||
&FuncInvocationKind::Internal(ref interpreter) => {
|
||||
match interpreter.state() {
|
||||
&InterpreterState::Resumable(ref value_type) => value_type.clone(),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
&FuncInvocationKind::Host { .. } => None,
|
||||
}
|
||||
}
|
||||
/// If the invocation is resumable, the expected return value type to be feed back in.
|
||||
pub fn resumable_value_type(&self) -> Option<ValueType> {
|
||||
match &self.kind {
|
||||
&FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
|
||||
&InterpreterState::Resumable(ref value_type) => value_type.clone(),
|
||||
_ => None,
|
||||
},
|
||||
&FuncInvocationKind::Host { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the invocation execution.
|
||||
pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
match self.kind {
|
||||
FuncInvocationKind::Internal(ref mut interpreter) => {
|
||||
if interpreter.state() != &InterpreterState::Initialized {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
Ok(interpreter.start_execution(externals)?)
|
||||
},
|
||||
FuncInvocationKind::Host { ref args, 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())?)
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Start the invocation execution.
|
||||
pub fn start_execution<'externals, E: Externals + 'externals>(
|
||||
&mut self,
|
||||
externals: &'externals mut E,
|
||||
) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
match self.kind {
|
||||
FuncInvocationKind::Internal(ref mut interpreter) => {
|
||||
if interpreter.state() != &InterpreterState::Initialized {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
Ok(interpreter.start_execution(externals)?)
|
||||
}
|
||||
FuncInvocationKind::Host {
|
||||
ref args,
|
||||
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.
|
||||
///
|
||||
/// `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
|
||||
/// [`is_resumable`]. Otherwise, a `NotResumable` error will be returned.
|
||||
///
|
||||
/// [`resumable_value_type`]: #method.resumable_value_type
|
||||
/// [`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> {
|
||||
match self.kind {
|
||||
FuncInvocationKind::Internal(ref mut interpreter) => {
|
||||
if !interpreter.state().is_resumable() {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
Ok(interpreter.resume_execution(return_val, externals)?)
|
||||
},
|
||||
FuncInvocationKind::Host { .. } => {
|
||||
return Err(ResumableError::NotResumable);
|
||||
},
|
||||
}
|
||||
}
|
||||
/// 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,
|
||||
/// `UnexpectedSignature` trap will be returned. The current invocation must also be resumable
|
||||
/// [`is_resumable`]. Otherwise, a `NotResumable` error will be returned.
|
||||
///
|
||||
/// [`resumable_value_type`]: #method.resumable_value_type
|
||||
/// [`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> {
|
||||
match self.kind {
|
||||
FuncInvocationKind::Internal(ref mut interpreter) => {
|
||||
if !interpreter.state().is_resumable() {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
Ok(interpreter.resume_execution(return_val, externals)?)
|
||||
}
|
||||
FuncInvocationKind::Host { .. } => {
|
||||
return Err(ResumableError::NotResumable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FuncBody {
|
||||
pub locals: Vec<Local>,
|
||||
pub code: isa::Instructions,
|
||||
pub locals: Vec<Local>,
|
||||
pub code: isa::Instructions,
|
||||
}
|
||||
|
|
104
src/global.rs
104
src/global.rs
|
@ -1,9 +1,9 @@
|
|||
use alloc::rc::Rc;
|
||||
use core::cell::Cell;
|
||||
use parity_wasm::elements::ValueType as EValueType;
|
||||
use types::ValueType;
|
||||
use value::RuntimeValue;
|
||||
use Error;
|
||||
use types::ValueType;
|
||||
use parity_wasm::elements::{ValueType as EValueType};
|
||||
|
||||
/// 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>);
|
||||
|
||||
impl ::core::ops::Deref for GlobalRef {
|
||||
type Target = GlobalInstance;
|
||||
fn deref(&self) -> &GlobalInstance {
|
||||
&self.0
|
||||
}
|
||||
type Target = GlobalInstance;
|
||||
fn deref(&self) -> &GlobalInstance {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalInstance {
|
||||
val: Cell<RuntimeValue>,
|
||||
mutable: bool,
|
||||
val: Cell<RuntimeValue>,
|
||||
mutable: bool,
|
||||
}
|
||||
|
||||
impl GlobalInstance {
|
||||
/// Allocate a global variable instance.
|
||||
///
|
||||
/// Since it is possible to export only immutable globals,
|
||||
/// users likely want to set `mutable` to `false`.
|
||||
pub fn alloc(val: RuntimeValue, mutable: bool) -> GlobalRef {
|
||||
GlobalRef(Rc::new(GlobalInstance {
|
||||
val: Cell::new(val),
|
||||
mutable,
|
||||
}))
|
||||
}
|
||||
/// Allocate a global variable instance.
|
||||
///
|
||||
/// Since it is possible to export only immutable globals,
|
||||
/// users likely want to set `mutable` to `false`.
|
||||
pub fn alloc(val: RuntimeValue, mutable: bool) -> GlobalRef {
|
||||
GlobalRef(Rc::new(GlobalInstance {
|
||||
val: Cell::new(val),
|
||||
mutable,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Change the value of this global variable.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if this global isn't mutable or if
|
||||
/// type of `val` doesn't match global's type.
|
||||
pub fn set(&self, val: RuntimeValue) -> Result<(), Error> {
|
||||
if !self.mutable {
|
||||
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()));
|
||||
}
|
||||
self.val.set(val);
|
||||
Ok(())
|
||||
}
|
||||
/// Change the value of this global variable.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if this global isn't mutable or if
|
||||
/// type of `val` doesn't match global's type.
|
||||
pub fn set(&self, val: RuntimeValue) -> Result<(), Error> {
|
||||
if !self.mutable {
|
||||
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()));
|
||||
}
|
||||
self.val.set(val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the value of this global variable.
|
||||
pub fn get(&self) -> RuntimeValue {
|
||||
self.val.get()
|
||||
}
|
||||
/// Get the value of this global variable.
|
||||
pub fn get(&self) -> RuntimeValue {
|
||||
self.val.get()
|
||||
}
|
||||
|
||||
/// Returns if this global variable is mutable.
|
||||
///
|
||||
/// Note: Imported and/or exported globals are always immutable.
|
||||
pub fn is_mutable(&self) -> bool {
|
||||
self.mutable
|
||||
}
|
||||
/// Returns if this global variable is mutable.
|
||||
///
|
||||
/// Note: Imported and/or exported globals are always immutable.
|
||||
pub fn is_mutable(&self) -> bool {
|
||||
self.mutable
|
||||
}
|
||||
|
||||
/// Returns value type of this global variable.
|
||||
pub fn value_type(&self) -> ValueType {
|
||||
self.val.get().value_type()
|
||||
}
|
||||
/// Returns value type of this global variable.
|
||||
pub fn value_type(&self) -> ValueType {
|
||||
self.val.get().value_type()
|
||||
}
|
||||
|
||||
pub(crate) fn elements_value_type(&self) -> EValueType {
|
||||
self.value_type().into_elements()
|
||||
}
|
||||
pub(crate) fn elements_value_type(&self) -> EValueType {
|
||||
self.value_type().into_elements()
|
||||
}
|
||||
}
|
||||
|
|
192
src/host.rs
192
src/host.rs
|
@ -1,6 +1,6 @@
|
|||
use core::any::TypeId;
|
||||
use value::{RuntimeValue, FromRuntimeValue};
|
||||
use {TrapKind, Trap};
|
||||
use value::{FromRuntimeValue, RuntimeValue};
|
||||
use {Trap, TrapKind};
|
||||
|
||||
/// Wrapper around slice of [`RuntimeValue`] for using it
|
||||
/// as an argument list conveniently.
|
||||
|
@ -10,55 +10,64 @@ use {TrapKind, Trap};
|
|||
pub struct RuntimeArgs<'a>(&'a [RuntimeValue]);
|
||||
|
||||
impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> {
|
||||
fn from(inner: &'a [RuntimeValue]) -> Self {
|
||||
RuntimeArgs(inner)
|
||||
}
|
||||
fn from(inner: &'a [RuntimeValue]) -> Self {
|
||||
RuntimeArgs(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRef<[RuntimeValue]> for RuntimeArgs<'a> {
|
||||
fn as_ref(&self) -> &[RuntimeValue] {
|
||||
self.0
|
||||
}
|
||||
fn as_ref(&self) -> &[RuntimeValue] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RuntimeArgs<'a> {
|
||||
/// Extract argument by index `idx`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if cast is invalid or not enough arguments.
|
||||
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap> where T: FromRuntimeValue {
|
||||
Ok(self.nth_value_checked(idx)?.try_into().ok_or_else(|| TrapKind::UnexpectedSignature)?)
|
||||
}
|
||||
/// Extract argument by index `idx`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if cast is invalid or not enough arguments.
|
||||
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap>
|
||||
where
|
||||
T: FromRuntimeValue,
|
||||
{
|
||||
Ok(self
|
||||
.nth_value_checked(idx)?
|
||||
.try_into()
|
||||
.ok_or_else(|| TrapKind::UnexpectedSignature)?)
|
||||
}
|
||||
|
||||
/// Extract argument as a [`RuntimeValue`] by index `idx`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if this list has not enough arguments.
|
||||
///
|
||||
/// [`RuntimeValue`]: enum.RuntimeValue.html
|
||||
pub fn nth_value_checked(&self, idx: usize) -> Result<RuntimeValue, Trap> {
|
||||
if self.0.len() <= idx {
|
||||
return Err(TrapKind::UnexpectedSignature.into());
|
||||
}
|
||||
Ok(self.0[idx])
|
||||
}
|
||||
/// Extract argument as a [`RuntimeValue`] by index `idx`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if this list has not enough arguments.
|
||||
///
|
||||
/// [`RuntimeValue`]: enum.RuntimeValue.html
|
||||
pub fn nth_value_checked(&self, idx: usize) -> Result<RuntimeValue, Trap> {
|
||||
if self.0.len() <= idx {
|
||||
return Err(TrapKind::UnexpectedSignature.into());
|
||||
}
|
||||
Ok(self.0[idx])
|
||||
}
|
||||
|
||||
/// Extract argument by index `idx`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if cast is invalid or not enough arguments.
|
||||
pub fn nth<T>(&self, idx: usize) -> T where T: FromRuntimeValue {
|
||||
let value = self.nth_value_checked(idx).expect("Invalid argument index");
|
||||
value.try_into().expect("Unexpected argument type")
|
||||
}
|
||||
/// Extract argument by index `idx`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if cast is invalid or not enough arguments.
|
||||
pub fn nth<T>(&self, idx: usize) -> T
|
||||
where
|
||||
T: FromRuntimeValue,
|
||||
{
|
||||
let value = self.nth_value_checked(idx).expect("Invalid argument index");
|
||||
value.try_into().expect("Unexpected argument type")
|
||||
}
|
||||
|
||||
/// Total number of arguments
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
/// Total number of arguments
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
#[doc(hidden)]
|
||||
fn __private_get_type_id__(&self) -> TypeId {
|
||||
TypeId::of::<Self>()
|
||||
}
|
||||
#[doc(hidden)]
|
||||
fn __private_get_type_id__(&self) -> TypeId {
|
||||
TypeId::of::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
impl HostError {
|
||||
/// Attempt to downcast this `HostError` to a concrete type by reference.
|
||||
pub fn downcast_ref<T: HostError>(&self) -> Option<&T> {
|
||||
if self.__private_get_type_id__() == TypeId::of::<T>() {
|
||||
unsafe { Some(&*(self as *const HostError as *const T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Attempt to downcast this `HostError` to a concrete type by reference.
|
||||
pub fn downcast_ref<T: HostError>(&self) -> Option<&T> {
|
||||
if self.__private_get_type_id__() == TypeId::of::<T>() {
|
||||
unsafe { Some(&*(self as *const HostError as *const T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to downcast this `HostError` to a concrete type by mutable
|
||||
/// reference.
|
||||
pub fn downcast_mut<T: HostError>(&mut self) -> Option<&mut T> {
|
||||
if self.__private_get_type_id__() == TypeId::of::<T>() {
|
||||
unsafe { Some(&mut *(self as *mut HostError as *mut T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Attempt to downcast this `HostError` to a concrete type by mutable
|
||||
/// reference.
|
||||
pub fn downcast_mut<T: HostError>(&mut self) -> Option<&mut T> {
|
||||
if self.__private_get_type_id__() == TypeId::of::<T>() {
|
||||
unsafe { Some(&mut *(self as *mut HostError as *mut T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that allows to implement host functions.
|
||||
|
@ -204,12 +213,12 @@ impl HostError {
|
|||
/// }
|
||||
/// ```
|
||||
pub trait Externals {
|
||||
/// Perform invoke of a host function by specified `index`.
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Trap>;
|
||||
/// Perform invoke of a host function by specified `index`.
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Trap>;
|
||||
}
|
||||
|
||||
/// Implementation of [`Externals`] that just traps on [`invoke_index`].
|
||||
|
@ -219,35 +228,34 @@ pub trait Externals {
|
|||
pub struct NopExternals;
|
||||
|
||||
impl Externals for NopExternals {
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
_index: usize,
|
||||
_args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
Err(TrapKind::Unreachable.into())
|
||||
}
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
_index: usize,
|
||||
_args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
Err(TrapKind::Unreachable.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use value::RuntimeValue;
|
||||
use super::{RuntimeArgs, HostError};
|
||||
use super::{HostError, RuntimeArgs};
|
||||
use value::RuntimeValue;
|
||||
|
||||
#[test]
|
||||
fn i32_runtime_args() {
|
||||
let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into();
|
||||
let val: i32 = args.nth_checked(0).unwrap();
|
||||
assert_eq!(val, 0);
|
||||
}
|
||||
#[test]
|
||||
fn i32_runtime_args() {
|
||||
let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into();
|
||||
let val: i32 = args.nth_checked(0).unwrap();
|
||||
assert_eq!(val, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i64_invalid_arg_cast() {
|
||||
let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into();
|
||||
assert!(args.nth_checked::<i32>(0).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn i64_invalid_arg_cast() {
|
||||
let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into();
|
||||
assert!(args.nth_checked::<i32>(0).is_err());
|
||||
}
|
||||
|
||||
// Tests that `HostError` trait is object safe.
|
||||
fn _host_error_is_object_safe(_: &HostError) {
|
||||
}
|
||||
// Tests that `HostError` trait is object safe.
|
||||
fn _host_error_is_object_safe(_: &HostError) {}
|
||||
}
|
||||
|
|
460
src/imports.rs
460
src/imports.rs
|
@ -1,20 +1,19 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashMap;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
use func::FuncRef;
|
||||
use global::GlobalRef;
|
||||
use memory::MemoryRef;
|
||||
use func::FuncRef;
|
||||
use table::TableRef;
|
||||
use module::ModuleRef;
|
||||
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||
use table::TableRef;
|
||||
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
|
||||
use {Error, Signature};
|
||||
|
||||
|
||||
/// Resolver of a module's dependencies.
|
||||
///
|
||||
/// 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
|
||||
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.
|
||||
///
|
||||
/// 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 global variable.
|
||||
///
|
||||
/// Returned global should match given `descriptor`, i.e. type and mutability
|
||||
/// should match. Otherwise, link-time error will occur.
|
||||
fn resolve_global(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
descriptor: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error>;
|
||||
|
||||
/// Resolve a global variable.
|
||||
///
|
||||
/// Returned global should match given `descriptor`, i.e. type and mutability
|
||||
/// should match. Otherwise, link-time error will occur.
|
||||
fn resolve_global(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
descriptor: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error>;
|
||||
/// Resolve a memory.
|
||||
///
|
||||
/// Returned memory should match requested memory (described by the `descriptor`),
|
||||
/// i.e. initial size of a returned memory should be equal or larger than requested memory.
|
||||
/// 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 memory doesn't match the requested then link-time error will occur.
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
descriptor: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error>;
|
||||
|
||||
/// Resolve a memory.
|
||||
///
|
||||
/// Returned memory should match requested memory (described by the `descriptor`),
|
||||
/// i.e. initial size of a returned memory should be equal or larger than requested memory.
|
||||
/// 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 memory doesn't match the requested then link-time error will occur.
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
descriptor: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, 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>;
|
||||
/// 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`].
|
||||
|
@ -108,216 +106,208 @@ pub trait ImportResolver {
|
|||
/// [`ImportResolver`]: trait.ImportResolver.html
|
||||
/// [`ModuleImportResolver`]: trait.ModuleImportResolver.html
|
||||
pub struct ImportsBuilder<'a> {
|
||||
modules: HashMap<String, &'a ModuleImportResolver>,
|
||||
modules: HashMap<String, &'a ModuleImportResolver>,
|
||||
}
|
||||
|
||||
impl<'a> Default for ImportsBuilder<'a> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ImportsBuilder<'a> {
|
||||
/// Create an empty `ImportsBuilder`.
|
||||
pub fn new() -> ImportsBuilder<'a> {
|
||||
ImportsBuilder { modules: HashMap::new() }
|
||||
}
|
||||
/// Create an empty `ImportsBuilder`.
|
||||
pub fn new() -> ImportsBuilder<'a> {
|
||||
ImportsBuilder {
|
||||
modules: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Register an resolver by a name.
|
||||
pub fn with_resolver<N: Into<String>>(
|
||||
mut self,
|
||||
name: N,
|
||||
resolver: &'a ModuleImportResolver,
|
||||
) -> Self {
|
||||
self.modules.insert(name.into(), resolver);
|
||||
self
|
||||
}
|
||||
/// Register an resolver by a name.
|
||||
pub fn with_resolver<N: Into<String>>(
|
||||
mut self,
|
||||
name: N,
|
||||
resolver: &'a ModuleImportResolver,
|
||||
) -> Self {
|
||||
self.modules.insert(name.into(), resolver);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register an resolver by a name.
|
||||
///
|
||||
/// Mutable borrowed version.
|
||||
pub fn push_resolver<N: Into<String>>(&mut self, name: N, resolver: &'a ModuleImportResolver) {
|
||||
self.modules.insert(name.into(), resolver);
|
||||
}
|
||||
/// Register an resolver by a name.
|
||||
///
|
||||
/// Mutable borrowed version.
|
||||
pub fn push_resolver<N: Into<String>>(&mut self, name: N, resolver: &'a ModuleImportResolver) {
|
||||
self.modules.insert(name.into(), resolver);
|
||||
}
|
||||
|
||||
fn resolver(&self, name: &str) -> Option<&ModuleImportResolver> {
|
||||
self.modules.get(name).cloned()
|
||||
}
|
||||
fn resolver(&self, name: &str) -> Option<&ModuleImportResolver> {
|
||||
self.modules.get(name).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ImportResolver for ImportsBuilder<'a> {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
self.resolver(module_name).ok_or_else(||
|
||||
Error::Instantiation(format!("Module {} not found", module_name))
|
||||
)?.resolve_func(field_name, signature)
|
||||
}
|
||||
fn resolve_func(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
self.resolver(module_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
|
||||
.resolve_func(field_name, signature)
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
self.resolver(module_name).ok_or_else(||
|
||||
Error::Instantiation(format!("Module {} not found", module_name))
|
||||
)?.resolve_global(field_name, global_type)
|
||||
}
|
||||
fn resolve_global(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
self.resolver(module_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
|
||||
.resolve_global(field_name, global_type)
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
self.resolver(module_name).ok_or_else(||
|
||||
Error::Instantiation(format!("Module {} not found", module_name))
|
||||
)?.resolve_memory(field_name, memory_type)
|
||||
}
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
self.resolver(module_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
|
||||
.resolve_memory(field_name, memory_type)
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
self.resolver(module_name).ok_or_else(||
|
||||
Error::Instantiation(format!("Module {} not found", module_name))
|
||||
)?.resolve_table(field_name, table_type)
|
||||
}
|
||||
fn resolve_table(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
self.resolver(module_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
|
||||
.resolve_table(field_name, table_type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Version of [`ImportResolver`] specialized for a single module.
|
||||
///
|
||||
/// [`ImportResolver`]: trait.ImportResolver.html
|
||||
pub trait ModuleImportResolver {
|
||||
/// Resolve a function.
|
||||
///
|
||||
/// See [`ImportResolver::resolve_func`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
}
|
||||
/// Resolve a function.
|
||||
///
|
||||
/// See [`ImportResolver::resolve_func`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
|
||||
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
|
||||
/// Resolve a global variable.
|
||||
///
|
||||
/// See [`ImportResolver::resolve_global`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global
|
||||
fn resolve_global(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
}
|
||||
/// Resolve a global variable.
|
||||
///
|
||||
/// See [`ImportResolver::resolve_global`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global
|
||||
fn resolve_global(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
|
||||
/// Resolve a memory.
|
||||
///
|
||||
/// See [`ImportResolver::resolve_memory`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
}
|
||||
/// Resolve a memory.
|
||||
///
|
||||
/// See [`ImportResolver::resolve_memory`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
|
||||
/// Resolve a table.
|
||||
///
|
||||
/// See [`ImportResolver::resolve_table`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
}
|
||||
/// Resolve a table.
|
||||
///
|
||||
/// See [`ImportResolver::resolve_table`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleImportResolver for ModuleRef {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
Ok(self.export_by_name(field_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} not found", field_name))
|
||||
})?
|
||||
.as_func()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} is not a function", field_name))
|
||||
})?)
|
||||
}
|
||||
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
|
||||
Ok(self
|
||||
.export_by_name(field_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||
.as_func()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} is not a function", field_name))
|
||||
})?)
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
Ok(self.export_by_name(field_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} not found", field_name))
|
||||
})?
|
||||
.as_global()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} is not a global", field_name))
|
||||
})?)
|
||||
}
|
||||
fn resolve_global(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
Ok(self
|
||||
.export_by_name(field_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||
.as_global()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} is not a global", field_name))
|
||||
})?)
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Ok(self.export_by_name(field_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} not found", field_name))
|
||||
})?
|
||||
.as_memory()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} is not a memory", field_name))
|
||||
})?)
|
||||
}
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Ok(self
|
||||
.export_by_name(field_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||
.as_memory()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} is not a memory", field_name))
|
||||
})?)
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
Ok(self.export_by_name(field_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} not found", field_name))
|
||||
})?
|
||||
.as_table()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} is not a table", field_name))
|
||||
})?)
|
||||
}
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
Ok(self
|
||||
.export_by_name(field_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||
.as_table()
|
||||
.cloned()
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} is not a table", field_name)))?)
|
||||
}
|
||||
}
|
||||
|
|
1198
src/isa.rs
1198
src/isa.rs
File diff suppressed because it is too large
Load Diff
674
src/lib.rs
674
src/lib.rs
|
@ -95,9 +95,7 @@
|
|||
//! ```
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
//// alloc is required in no_std
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
|
@ -117,11 +115,11 @@ extern crate wabt;
|
|||
#[macro_use]
|
||||
extern crate assert_matches;
|
||||
|
||||
extern crate parity_wasm;
|
||||
extern crate byteorder;
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate hashmap_core;
|
||||
extern crate memory_units as memory_units_crate;
|
||||
extern crate parity_wasm;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
@ -138,32 +136,32 @@ extern crate libm;
|
|||
/// Traps can't be handled by WebAssembly code, but are reported to the embedder.
|
||||
#[derive(Debug)]
|
||||
pub struct Trap {
|
||||
kind: TrapKind,
|
||||
kind: TrapKind,
|
||||
}
|
||||
|
||||
impl Trap {
|
||||
/// Create new trap.
|
||||
pub fn new(kind: TrapKind) -> Trap {
|
||||
Trap { kind }
|
||||
}
|
||||
/// Create new trap.
|
||||
pub fn new(kind: TrapKind) -> Trap {
|
||||
Trap { kind }
|
||||
}
|
||||
|
||||
/// Returns kind of this trap.
|
||||
pub fn kind(&self) -> &TrapKind {
|
||||
&self.kind
|
||||
}
|
||||
/// Returns kind of this trap.
|
||||
pub fn kind(&self) -> &TrapKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Trap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Trap: {:?}", self.kind)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Trap: {:?}", self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl error::Error for Trap {
|
||||
fn description(&self) -> &str {
|
||||
"runtime trap"
|
||||
}
|
||||
fn description(&self) -> &str {
|
||||
"runtime trap"
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
#[derive(Debug)]
|
||||
pub enum TrapKind {
|
||||
/// Wasm code executed `unreachable` opcode.
|
||||
///
|
||||
/// `unreachable` is a special opcode which always traps upon execution.
|
||||
/// This opcode have a similar purpose as `ud2` in x86.
|
||||
Unreachable,
|
||||
/// Wasm code executed `unreachable` opcode.
|
||||
///
|
||||
/// `unreachable` is a special opcode which always traps upon execution.
|
||||
/// This opcode have a similar purpose as `ud2` in x86.
|
||||
Unreachable,
|
||||
|
||||
/// Attempt to load or store at the address which
|
||||
/// lies outside of bounds of the memory.
|
||||
///
|
||||
/// Since addresses are interpreted as unsigned integers, out of bounds access
|
||||
/// can't happen with negative addresses (i.e. they will always wrap).
|
||||
MemoryAccessOutOfBounds,
|
||||
/// Attempt to load or store at the address which
|
||||
/// lies outside of bounds of the memory.
|
||||
///
|
||||
/// Since addresses are interpreted as unsigned integers, out of bounds access
|
||||
/// can't happen with negative addresses (i.e. they will always wrap).
|
||||
MemoryAccessOutOfBounds,
|
||||
|
||||
/// Attempt to access table element at index which
|
||||
/// lies outside of bounds.
|
||||
///
|
||||
/// This typically can happen when `call_indirect` is executed
|
||||
/// with index that lies out of bounds.
|
||||
///
|
||||
/// Since indexes are interpreted as unsinged integers, out of bounds access
|
||||
/// can't happen with negative indexes (i.e. they will always wrap).
|
||||
TableAccessOutOfBounds,
|
||||
/// Attempt to access table element at index which
|
||||
/// lies outside of bounds.
|
||||
///
|
||||
/// This typically can happen when `call_indirect` is executed
|
||||
/// with index that lies out of bounds.
|
||||
///
|
||||
/// Since indexes are interpreted as unsinged integers, out of bounds access
|
||||
/// can't happen with negative indexes (i.e. they will always wrap).
|
||||
TableAccessOutOfBounds,
|
||||
|
||||
/// Attempt to access table element which is uninitialized (i.e. `None`).
|
||||
///
|
||||
/// This typically can happen when `call_indirect` is executed.
|
||||
ElemUninitialized,
|
||||
/// Attempt to access table element which is uninitialized (i.e. `None`).
|
||||
///
|
||||
/// This typically can happen when `call_indirect` is executed.
|
||||
ElemUninitialized,
|
||||
|
||||
/// Attempt to divide by zero.
|
||||
///
|
||||
/// This trap typically can happen if `div` or `rem` is executed with
|
||||
/// zero as divider.
|
||||
DivisionByZero,
|
||||
/// Attempt to divide by zero.
|
||||
///
|
||||
/// This trap typically can happen if `div` or `rem` is executed with
|
||||
/// zero as divider.
|
||||
DivisionByZero,
|
||||
|
||||
/// Attempt to make a conversion to an int failed.
|
||||
///
|
||||
/// This can happen when:
|
||||
///
|
||||
/// - 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.
|
||||
/// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
|
||||
InvalidConversionToInt,
|
||||
/// Attempt to make a conversion to an int failed.
|
||||
///
|
||||
/// This can happen when:
|
||||
///
|
||||
/// - 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.
|
||||
/// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
|
||||
InvalidConversionToInt,
|
||||
|
||||
/// Stack overflow.
|
||||
///
|
||||
/// This is likely caused by some infinite or very deep recursion.
|
||||
/// Extensive inlining might also be the cause of stack overflow.
|
||||
StackOverflow,
|
||||
/// Stack overflow.
|
||||
///
|
||||
/// This is likely caused by some infinite or very deep recursion.
|
||||
/// Extensive inlining might also be the cause of stack overflow.
|
||||
StackOverflow,
|
||||
|
||||
/// Attempt to invoke a function with mismatching signature.
|
||||
///
|
||||
/// This can happen if [`FuncInstance`] was invoked
|
||||
/// with mismatching [signature][`Signature`].
|
||||
///
|
||||
/// This can always happen with indirect calls. `call_indirect` instruction always
|
||||
/// specifies the expected signature of function. If `call_indirect` is executed
|
||||
/// with index that points on function with signature different that is
|
||||
/// expected by this `call_indirect`, this trap is raised.
|
||||
///
|
||||
/// [`Signature`]: struct.Signature.html
|
||||
UnexpectedSignature,
|
||||
/// Attempt to invoke a function with mismatching signature.
|
||||
///
|
||||
/// This can happen if [`FuncInstance`] was invoked
|
||||
/// with mismatching [signature][`Signature`].
|
||||
///
|
||||
/// This can always happen with indirect calls. `call_indirect` instruction always
|
||||
/// specifies the expected signature of function. If `call_indirect` is executed
|
||||
/// with index that points on function with signature different that is
|
||||
/// expected by this `call_indirect`, this trap is raised.
|
||||
///
|
||||
/// [`Signature`]: struct.Signature.html
|
||||
UnexpectedSignature,
|
||||
|
||||
/// Error specified by the host.
|
||||
///
|
||||
/// Typically returned from an implementation of [`Externals`].
|
||||
///
|
||||
/// [`Externals`]: trait.Externals.html
|
||||
Host(Box<host::HostError>),
|
||||
/// Error specified by the host.
|
||||
///
|
||||
/// Typically returned from an implementation of [`Externals`].
|
||||
///
|
||||
/// [`Externals`]: trait.Externals.html
|
||||
Host(Box<host::HostError>),
|
||||
}
|
||||
|
||||
impl TrapKind {
|
||||
/// Whether this trap is specified by the host.
|
||||
pub fn is_host(&self) -> bool {
|
||||
match self {
|
||||
&TrapKind::Host(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
/// Whether this trap is specified by the host.
|
||||
pub fn is_host(&self) -> bool {
|
||||
match self {
|
||||
&TrapKind::Host(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal interpreter error.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Module validation error. Might occur only at load time.
|
||||
Validation(String),
|
||||
/// Error while instantiating a module. Might occur when provided
|
||||
/// with incorrect exports (i.e. linkage failure).
|
||||
Instantiation(String),
|
||||
/// Function-level error.
|
||||
Function(String),
|
||||
/// Table-level error.
|
||||
Table(String),
|
||||
/// Memory-level error.
|
||||
Memory(String),
|
||||
/// Global-level error.
|
||||
Global(String),
|
||||
/// Value-level error.
|
||||
Value(String),
|
||||
/// Trap.
|
||||
Trap(Trap),
|
||||
/// Custom embedder error.
|
||||
Host(Box<host::HostError>),
|
||||
/// Module validation error. Might occur only at load time.
|
||||
Validation(String),
|
||||
/// Error while instantiating a module. Might occur when provided
|
||||
/// with incorrect exports (i.e. linkage failure).
|
||||
Instantiation(String),
|
||||
/// Function-level error.
|
||||
Function(String),
|
||||
/// Table-level error.
|
||||
Table(String),
|
||||
/// Memory-level error.
|
||||
Memory(String),
|
||||
/// Global-level error.
|
||||
Global(String),
|
||||
/// Value-level error.
|
||||
Value(String),
|
||||
/// Trap.
|
||||
Trap(Trap),
|
||||
/// Custom embedder error.
|
||||
Host(Box<host::HostError>),
|
||||
}
|
||||
|
||||
impl 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.
|
||||
///
|
||||
/// [`HostError`]: trait.HostError.html
|
||||
/// [`Host`]: enum.Error.html#variant.Host
|
||||
/// [`Trap`]: enum.Error.html#variant.Trap
|
||||
/// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
|
||||
pub fn as_host_error(&self) -> Option<&host::HostError> {
|
||||
match *self {
|
||||
Error::Host(ref host_err) => Some(&**host_err),
|
||||
Error::Trap(ref trap) => match *trap.kind() {
|
||||
TrapKind::Host(ref host_err) => Some(&**host_err),
|
||||
_ => None,
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// 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.
|
||||
///
|
||||
/// [`HostError`]: trait.HostError.html
|
||||
/// [`Host`]: enum.Error.html#variant.Host
|
||||
/// [`Trap`]: enum.Error.html#variant.Trap
|
||||
/// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
|
||||
pub fn as_host_error(&self) -> Option<&host::HostError> {
|
||||
match *self {
|
||||
Error::Host(ref host_err) => Some(&**host_err),
|
||||
Error::Trap(ref trap) => match *trap.kind() {
|
||||
TrapKind::Host(ref host_err) => Some(&**host_err),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Error {
|
||||
fn into(self) -> String {
|
||||
match self {
|
||||
Error::Validation(s) => s,
|
||||
Error::Instantiation(s) => s,
|
||||
Error::Function(s) => s,
|
||||
Error::Table(s) => s,
|
||||
Error::Memory(s) => s,
|
||||
Error::Global(s) => s,
|
||||
Error::Value(s) => s,
|
||||
Error::Trap(s) => format!("trap: {:?}", s),
|
||||
Error::Host(e) => format!("user: {}", e),
|
||||
}
|
||||
}
|
||||
fn into(self) -> String {
|
||||
match self {
|
||||
Error::Validation(s) => s,
|
||||
Error::Instantiation(s) => s,
|
||||
Error::Function(s) => s,
|
||||
Error::Table(s) => s,
|
||||
Error::Memory(s) => s,
|
||||
Error::Global(s) => s,
|
||||
Error::Value(s) => s,
|
||||
Error::Trap(s) => format!("trap: {:?}", s),
|
||||
Error::Host(e) => format!("user: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::Validation(ref s) => write!(f, "Validation: {}", s),
|
||||
Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s),
|
||||
Error::Function(ref s) => write!(f, "Function: {}", s),
|
||||
Error::Table(ref s) => write!(f, "Table: {}", s),
|
||||
Error::Memory(ref s) => write!(f, "Memory: {}", s),
|
||||
Error::Global(ref s) => write!(f, "Global: {}", s),
|
||||
Error::Value(ref s) => write!(f, "Value: {}", s),
|
||||
Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
|
||||
Error::Host(ref e) => write!(f, "User: {}", e),
|
||||
}
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::Validation(ref s) => write!(f, "Validation: {}", s),
|
||||
Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s),
|
||||
Error::Function(ref s) => write!(f, "Function: {}", s),
|
||||
Error::Table(ref s) => write!(f, "Table: {}", s),
|
||||
Error::Memory(ref s) => write!(f, "Memory: {}", s),
|
||||
Error::Global(ref s) => write!(f, "Global: {}", s),
|
||||
Error::Value(ref s) => write!(f, "Value: {}", s),
|
||||
Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
|
||||
Error::Host(ref e) => write!(f, "User: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::Validation(ref s) => s,
|
||||
Error::Instantiation(ref s) => s,
|
||||
Error::Function(ref s) => s,
|
||||
Error::Table(ref s) => s,
|
||||
Error::Memory(ref s) => s,
|
||||
Error::Global(ref s) => s,
|
||||
Error::Value(ref s) => s,
|
||||
Error::Trap(_) => "Trap",
|
||||
Error::Host(_) => "Host error",
|
||||
}
|
||||
}
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::Validation(ref s) => s,
|
||||
Error::Instantiation(ref s) => s,
|
||||
Error::Function(ref s) => s,
|
||||
Error::Table(ref s) => s,
|
||||
Error::Memory(ref s) => s,
|
||||
Error::Global(ref s) => s,
|
||||
Error::Value(ref s) => s,
|
||||
Error::Trap(_) => "Trap",
|
||||
Error::Host(_) => "Host error",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> From<U> for Error where U: host::HostError + Sized {
|
||||
fn from(e: U) -> Self {
|
||||
Error::Host(Box::new(e))
|
||||
}
|
||||
impl<U> From<U> for Error
|
||||
where
|
||||
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 {
|
||||
fn from(e: U) -> Self {
|
||||
Trap::new(TrapKind::Host(Box::new(e)))
|
||||
}
|
||||
impl<U> From<U> for Trap
|
||||
where
|
||||
U: host::HostError + Sized,
|
||||
{
|
||||
fn from(e: U) -> Self {
|
||||
Trap::new(TrapKind::Host(Box::new(e)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Trap> for Error {
|
||||
fn from(e: Trap) -> Error {
|
||||
Error::Trap(e)
|
||||
}
|
||||
fn from(e: Trap) -> Error {
|
||||
Error::Trap(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TrapKind> for Trap {
|
||||
fn from(e: TrapKind) -> Trap {
|
||||
Trap::new(e)
|
||||
}
|
||||
fn from(e: TrapKind) -> Trap {
|
||||
Trap::new(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<validation::Error> for Error {
|
||||
fn from(e: validation::Error) -> Error {
|
||||
Error::Validation(e.to_string())
|
||||
}
|
||||
fn from(e: validation::Error) -> Error {
|
||||
Error::Validation(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
mod validation;
|
||||
mod common;
|
||||
mod memory;
|
||||
mod module;
|
||||
mod runner;
|
||||
mod table;
|
||||
mod value;
|
||||
mod func;
|
||||
mod global;
|
||||
mod host;
|
||||
mod imports;
|
||||
mod global;
|
||||
mod func;
|
||||
mod types;
|
||||
mod isa;
|
||||
mod memory;
|
||||
mod module;
|
||||
pub mod nan_preserving_float;
|
||||
mod runner;
|
||||
mod table;
|
||||
mod types;
|
||||
mod validation;
|
||||
mod value;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
|
||||
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::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError};
|
||||
pub use self::global::{GlobalInstance, GlobalRef};
|
||||
pub use self::func::{FuncInstance, FuncRef, FuncInvocation, ResumableError};
|
||||
pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||
pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs};
|
||||
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.
|
||||
pub mod memory_units {
|
||||
pub use memory_units_crate::wasm32::*;
|
||||
pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of};
|
||||
pub use memory_units_crate::wasm32::*;
|
||||
pub use memory_units_crate::{size_of, ByteSize, Bytes, RoundUpTo};
|
||||
}
|
||||
|
||||
/// Deserialized module prepared for instantiation.
|
||||
pub struct Module {
|
||||
code_map: Vec<isa::Instructions>,
|
||||
module: parity_wasm::elements::Module,
|
||||
code_map: Vec<isa::Instructions>,
|
||||
module: parity_wasm::elements::Module,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
/// Create `Module` from `parity_wasm::elements::Module`.
|
||||
///
|
||||
/// This function will load, validate and prepare a `parity_wasm`'s `Module`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if provided `Module` is not valid.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate parity_wasm;
|
||||
/// extern crate wasmi;
|
||||
///
|
||||
/// use parity_wasm::builder;
|
||||
/// use parity_wasm::elements;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let parity_module =
|
||||
/// builder::module()
|
||||
/// .function()
|
||||
/// .signature().with_param(elements::ValueType::I32).build()
|
||||
/// .body().build()
|
||||
/// .build()
|
||||
/// .build();
|
||||
///
|
||||
/// let module = wasmi::Module::from_parity_wasm_module(parity_module)
|
||||
/// .expect("parity-wasm builder generated invalid module!");
|
||||
///
|
||||
/// // Instantiate `module`, etc...
|
||||
/// }
|
||||
/// ```
|
||||
pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> {
|
||||
use validation::{validate_module, ValidatedModule};
|
||||
let ValidatedModule {
|
||||
code_map,
|
||||
module,
|
||||
} = validate_module(module)?;
|
||||
/// Create `Module` from `parity_wasm::elements::Module`.
|
||||
///
|
||||
/// This function will load, validate and prepare a `parity_wasm`'s `Module`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if provided `Module` is not valid.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate parity_wasm;
|
||||
/// extern crate wasmi;
|
||||
///
|
||||
/// use parity_wasm::builder;
|
||||
/// use parity_wasm::elements;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let parity_module =
|
||||
/// builder::module()
|
||||
/// .function()
|
||||
/// .signature().with_param(elements::ValueType::I32).build()
|
||||
/// .body().build()
|
||||
/// .build()
|
||||
/// .build();
|
||||
///
|
||||
/// let module = wasmi::Module::from_parity_wasm_module(parity_module)
|
||||
/// .expect("parity-wasm builder generated invalid module!");
|
||||
///
|
||||
/// // Instantiate `module`, etc...
|
||||
/// }
|
||||
/// ```
|
||||
pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> {
|
||||
use validation::{validate_module, ValidatedModule};
|
||||
let ValidatedModule { code_map, module } = validate_module(module)?;
|
||||
|
||||
Ok(Module {
|
||||
code_map,
|
||||
module,
|
||||
})
|
||||
}
|
||||
Ok(Module { code_map, module })
|
||||
}
|
||||
|
||||
/// Fail if the module contains any floating-point operations
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if provided `Module` is not valid.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate wasmi;
|
||||
/// # extern crate wabt;
|
||||
///
|
||||
/// let wasm_binary: Vec<u8> =
|
||||
/// wabt::wat2wasm(
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (func $add (param $lhs i32) (param $rhs i32) (result i32)
|
||||
/// get_local $lhs
|
||||
/// get_local $rhs
|
||||
/// i32.add))
|
||||
/// "#,
|
||||
/// )
|
||||
/// .expect("failed to parse wat");
|
||||
///
|
||||
/// // Load wasm binary and prepare it for instantiation.
|
||||
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
|
||||
/// assert!(module.deny_floating_point().is_ok());
|
||||
///
|
||||
/// let wasm_binary: Vec<u8> =
|
||||
/// wabt::wat2wasm(
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (func $add (param $lhs f32) (param $rhs f32) (result f32)
|
||||
/// get_local $lhs
|
||||
/// get_local $rhs
|
||||
/// f32.add))
|
||||
/// "#,
|
||||
/// )
|
||||
/// .expect("failed to parse wat");
|
||||
///
|
||||
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
|
||||
/// assert!(module.deny_floating_point().is_err());
|
||||
///
|
||||
/// let wasm_binary: Vec<u8> =
|
||||
/// wabt::wat2wasm(
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (func $add (param $lhs f32) (param $rhs f32) (result f32)
|
||||
/// get_local $lhs))
|
||||
/// "#,
|
||||
/// )
|
||||
/// .expect("failed to parse wat");
|
||||
///
|
||||
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
|
||||
/// assert!(module.deny_floating_point().is_err());
|
||||
/// ```
|
||||
pub fn deny_floating_point(&self) -> Result<(), Error> {
|
||||
validation::deny_floating_point(&self.module).map_err(Into::into)
|
||||
}
|
||||
/// Fail if the module contains any floating-point operations
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if provided `Module` is not valid.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate wasmi;
|
||||
/// # extern crate wabt;
|
||||
///
|
||||
/// let wasm_binary: Vec<u8> =
|
||||
/// wabt::wat2wasm(
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (func $add (param $lhs i32) (param $rhs i32) (result i32)
|
||||
/// get_local $lhs
|
||||
/// get_local $rhs
|
||||
/// i32.add))
|
||||
/// "#,
|
||||
/// )
|
||||
/// .expect("failed to parse wat");
|
||||
///
|
||||
/// // Load wasm binary and prepare it for instantiation.
|
||||
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
|
||||
/// assert!(module.deny_floating_point().is_ok());
|
||||
///
|
||||
/// let wasm_binary: Vec<u8> =
|
||||
/// wabt::wat2wasm(
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (func $add (param $lhs f32) (param $rhs f32) (result f32)
|
||||
/// get_local $lhs
|
||||
/// get_local $rhs
|
||||
/// f32.add))
|
||||
/// "#,
|
||||
/// )
|
||||
/// .expect("failed to parse wat");
|
||||
///
|
||||
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
|
||||
/// assert!(module.deny_floating_point().is_err());
|
||||
///
|
||||
/// let wasm_binary: Vec<u8> =
|
||||
/// wabt::wat2wasm(
|
||||
/// r#"
|
||||
/// (module
|
||||
/// (func $add (param $lhs f32) (param $rhs f32) (result f32)
|
||||
/// get_local $lhs))
|
||||
/// "#,
|
||||
/// )
|
||||
/// .expect("failed to parse wat");
|
||||
///
|
||||
/// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
|
||||
/// assert!(module.deny_floating_point().is_err());
|
||||
/// ```
|
||||
pub fn deny_floating_point(&self) -> Result<(), Error> {
|
||||
validation::deny_floating_point(&self.module).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Create `Module` from a given buffer.
|
||||
///
|
||||
/// This function will deserialize wasm module from a given module,
|
||||
/// validate and prepare it for instantiation.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate wasmi;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let module =
|
||||
/// wasmi::Module::from_buffer(
|
||||
/// // Minimal module:
|
||||
/// // \0asm - magic
|
||||
/// // 0x01 - version (in little-endian)
|
||||
/// &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]
|
||||
/// ).expect("Failed to load minimal module");
|
||||
///
|
||||
/// // Instantiate `module`, etc...
|
||||
/// }
|
||||
/// ```
|
||||
pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> {
|
||||
let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref())
|
||||
.map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?;
|
||||
Module::from_parity_wasm_module(module)
|
||||
}
|
||||
/// Create `Module` from a given buffer.
|
||||
///
|
||||
/// This function will deserialize wasm module from a given module,
|
||||
/// validate and prepare it for instantiation.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate wasmi;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let module =
|
||||
/// wasmi::Module::from_buffer(
|
||||
/// // Minimal module:
|
||||
/// // \0asm - magic
|
||||
/// // 0x01 - version (in little-endian)
|
||||
/// &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]
|
||||
/// ).expect("Failed to load minimal module");
|
||||
///
|
||||
/// // Instantiate `module`, etc...
|
||||
/// }
|
||||
/// ```
|
||||
pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> {
|
||||
let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref())
|
||||
.map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?;
|
||||
Module::from_parity_wasm_module(module)
|
||||
}
|
||||
|
||||
pub(crate) fn module(&self) -> &parity_wasm::elements::Module {
|
||||
&self.module
|
||||
}
|
||||
pub(crate) fn module(&self) -> &parity_wasm::elements::Module {
|
||||
&self.module
|
||||
}
|
||||
|
||||
pub(crate) fn code(&self) -> &Vec<isa::Instructions> {
|
||||
&self.code_map
|
||||
}
|
||||
pub(crate) fn code(&self) -> &Vec<isa::Instructions> {
|
||||
&self.code_map
|
||||
}
|
||||
}
|
||||
|
|
1092
src/memory.rs
1092
src/memory.rs
File diff suppressed because it is too large
Load Diff
1324
src/module.rs
1324
src/module.rs
File diff suppressed because it is too large
Load Diff
|
@ -3,8 +3,8 @@
|
|||
#[cfg(not(feature = "std"))]
|
||||
use libm::{F32Ext, F64Ext};
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub, Rem};
|
||||
use core::cmp::{Ordering, PartialEq, PartialOrd};
|
||||
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
|
||||
|
||||
macro_rules! impl_binop {
|
||||
($for:ident, $is:ident, $op:ident, $func_name:ident) => {
|
||||
|
@ -13,19 +13,22 @@ macro_rules! impl_binop {
|
|||
|
||||
fn $func_name(self, other: T) -> Self {
|
||||
$for(
|
||||
$op::$func_name(
|
||||
$is::from_bits(self.0),
|
||||
$is::from_bits(other.into().0)
|
||||
).to_bits()
|
||||
$op::$func_name($is::from_bits(self.0), $is::from_bits(other.into().0))
|
||||
.to_bits(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! float {
|
||||
($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) => {
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -112,7 +115,7 @@ macro_rules! float {
|
|||
$is::from(*self).fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
float!(F32, u32, f32);
|
||||
|
@ -150,9 +153,9 @@ mod tests {
|
|||
|
||||
use super::{F32, F64};
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::fmt::Debug;
|
||||
use core::iter;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
|
||||
fn test_ops<T, F, I>(iter: I)
|
||||
where
|
||||
|
|
2521
src/runner.rs
2521
src/runner.rs
File diff suppressed because it is too large
Load Diff
216
src/table.rs
216
src/table.rs
|
@ -1,13 +1,13 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::u32;
|
||||
use core::fmt;
|
||||
use core::cell::RefCell;
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
use core::fmt;
|
||||
use core::u32;
|
||||
use func::FuncRef;
|
||||
use module::check_limits;
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
|
||||
/// Reference to a table (See [`TableInstance`] for details).
|
||||
///
|
||||
|
@ -19,10 +19,10 @@ use module::check_limits;
|
|||
pub struct TableRef(Rc<TableInstance>);
|
||||
|
||||
impl ::core::ops::Deref for TableRef {
|
||||
type Target = TableInstance;
|
||||
fn deref(&self) -> &TableInstance {
|
||||
&self.0
|
||||
}
|
||||
type Target = TableInstance;
|
||||
fn deref(&self) -> &TableInstance {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime representation of a table.
|
||||
|
@ -39,118 +39,118 @@ impl ::core::ops::Deref for TableRef {
|
|||
/// [`grow`]: #method.grow
|
||||
///
|
||||
pub struct TableInstance {
|
||||
/// Table limits.
|
||||
limits: ResizableLimits,
|
||||
/// Table memory buffer.
|
||||
buffer: RefCell<Vec<Option<FuncRef>>>,
|
||||
/// Table limits.
|
||||
limits: ResizableLimits,
|
||||
/// Table memory buffer.
|
||||
buffer: RefCell<Vec<Option<FuncRef>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TableInstance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("TableInstance")
|
||||
.field("limits", &self.limits)
|
||||
.field("buffer.len", &self.buffer.borrow().len())
|
||||
.finish()
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("TableInstance")
|
||||
.field("limits", &self.limits)
|
||||
.field("buffer.len", &self.buffer.borrow().len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl TableInstance {
|
||||
/// Allocate a table instance.
|
||||
///
|
||||
/// The table allocated with initial size, specified by `initial_size`.
|
||||
/// Maximum size can be specified by `maximum_size`.
|
||||
///
|
||||
/// All table elements are allocated uninitialized.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if `initial_size` is greater than `maximum_size`.
|
||||
pub fn alloc(initial_size: u32, maximum_size: Option<u32>) -> Result<TableRef, Error> {
|
||||
let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?;
|
||||
Ok(TableRef(Rc::new(table)))
|
||||
}
|
||||
/// Allocate a table instance.
|
||||
///
|
||||
/// The table allocated with initial size, specified by `initial_size`.
|
||||
/// Maximum size can be specified by `maximum_size`.
|
||||
///
|
||||
/// All table elements are allocated uninitialized.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if `initial_size` is greater than `maximum_size`.
|
||||
pub fn alloc(initial_size: u32, maximum_size: Option<u32>) -> Result<TableRef, Error> {
|
||||
let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?;
|
||||
Ok(TableRef(Rc::new(table)))
|
||||
}
|
||||
|
||||
fn new(limits: ResizableLimits) -> Result<TableInstance, Error> {
|
||||
check_limits(&limits)?;
|
||||
Ok(TableInstance {
|
||||
buffer: RefCell::new(vec![None; limits.initial() as usize]),
|
||||
limits: limits,
|
||||
})
|
||||
}
|
||||
fn new(limits: ResizableLimits) -> Result<TableInstance, Error> {
|
||||
check_limits(&limits)?;
|
||||
Ok(TableInstance {
|
||||
buffer: RefCell::new(vec![None; limits.initial() as usize]),
|
||||
limits: limits,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return table limits.
|
||||
pub(crate) fn limits(&self) -> &ResizableLimits {
|
||||
&self.limits
|
||||
}
|
||||
/// Return table limits.
|
||||
pub(crate) fn limits(&self) -> &ResizableLimits {
|
||||
&self.limits
|
||||
}
|
||||
|
||||
/// Returns size this table was created with.
|
||||
pub fn initial_size(&self) -> u32 {
|
||||
self.limits.initial()
|
||||
}
|
||||
/// Returns size this table was created with.
|
||||
pub fn initial_size(&self) -> u32 {
|
||||
self.limits.initial()
|
||||
}
|
||||
|
||||
/// Returns maximum size `TableInstance` can grow to.
|
||||
pub fn maximum_size(&self) -> Option<u32> {
|
||||
self.limits.maximum()
|
||||
}
|
||||
/// Returns maximum size `TableInstance` can grow to.
|
||||
pub fn maximum_size(&self) -> Option<u32> {
|
||||
self.limits.maximum()
|
||||
}
|
||||
|
||||
/// Returns current size of the table.
|
||||
pub fn current_size(&self) -> u32 {
|
||||
self.buffer.borrow().len() as u32
|
||||
}
|
||||
/// Returns current size of the table.
|
||||
pub fn current_size(&self) -> u32 {
|
||||
self.buffer.borrow().len() as u32
|
||||
}
|
||||
|
||||
/// Increases the size of the table by given number of elements.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if tried to allocate more elements than permited by limit.
|
||||
pub fn grow(&self, by: u32) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let maximum_size = self.maximum_size().unwrap_or(u32::MAX);
|
||||
let new_size = self.current_size().checked_add(by)
|
||||
.and_then(|new_size| {
|
||||
if maximum_size < new_size {
|
||||
None
|
||||
} else {
|
||||
Some(new_size)
|
||||
}
|
||||
})
|
||||
.ok_or_else(||
|
||||
Error::Table(format!(
|
||||
"Trying to grow table by {} items when there are already {} items",
|
||||
by,
|
||||
self.current_size(),
|
||||
))
|
||||
)?;
|
||||
buffer.resize(new_size as usize, None);
|
||||
Ok(())
|
||||
}
|
||||
/// Increases the size of the table by given number of elements.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if tried to allocate more elements than permited by limit.
|
||||
pub fn grow(&self, by: u32) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let maximum_size = self.maximum_size().unwrap_or(u32::MAX);
|
||||
let new_size = self
|
||||
.current_size()
|
||||
.checked_add(by)
|
||||
.and_then(|new_size| {
|
||||
if maximum_size < new_size {
|
||||
None
|
||||
} else {
|
||||
Some(new_size)
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"Trying to grow table by {} items when there are already {} items",
|
||||
by,
|
||||
self.current_size(),
|
||||
))
|
||||
})?;
|
||||
buffer.resize(new_size as usize, None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the specific value in the table
|
||||
pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> {
|
||||
let buffer = self.buffer.borrow();
|
||||
let buffer_len = buffer.len();
|
||||
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(||
|
||||
Error::Table(format!(
|
||||
"trying to read table item with index {} when there are only {} items",
|
||||
offset,
|
||||
buffer_len
|
||||
)),
|
||||
)?;
|
||||
Ok(table_elem)
|
||||
}
|
||||
/// Get the specific value in the table
|
||||
pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> {
|
||||
let buffer = self.buffer.borrow();
|
||||
let buffer_len = buffer.len();
|
||||
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"trying to read table item with index {} when there are only {} items",
|
||||
offset, buffer_len
|
||||
))
|
||||
})?;
|
||||
Ok(table_elem)
|
||||
}
|
||||
|
||||
/// Set the table element to the specified function.
|
||||
pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let buffer_len = buffer.len();
|
||||
let table_elem = buffer.get_mut(offset as usize).ok_or_else(||
|
||||
Error::Table(format!(
|
||||
"trying to update table item with index {} when there are only {} items",
|
||||
offset,
|
||||
buffer_len
|
||||
))
|
||||
)?;
|
||||
*table_elem = value;
|
||||
Ok(())
|
||||
}
|
||||
/// Set the table element to the specified function.
|
||||
pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let buffer_len = buffer.len();
|
||||
let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"trying to update table item with index {} when there are only {} items",
|
||||
offset, buffer_len
|
||||
))
|
||||
})?;
|
||||
*table_elem = value;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
use wabt;
|
||||
use {Module};
|
||||
use Module;
|
||||
|
||||
mod host;
|
||||
mod wasm;
|
||||
|
@ -12,25 +12,31 @@ fn assert_std_err_impl<T: ::std::error::Error>() {}
|
|||
|
||||
#[test]
|
||||
fn assert_error_properties() {
|
||||
assert_send::<Error>();
|
||||
assert_sync::<Error>();
|
||||
assert_std_err_impl::<Error>();
|
||||
assert_send::<Error>();
|
||||
assert_sync::<Error>();
|
||||
assert_std_err_impl::<Error>();
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[test]
|
||||
fn unsigned_to_runtime_value() {
|
||||
use super::RuntimeValue;
|
||||
use super::RuntimeValue;
|
||||
|
||||
let overflow_i32: u32 = ::core::i32::MAX as u32 + 1;
|
||||
assert_eq!(RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(), overflow_i32);
|
||||
let overflow_i32: u32 = ::core::i32::MAX as u32 + 1;
|
||||
assert_eq!(
|
||||
RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(),
|
||||
overflow_i32
|
||||
);
|
||||
|
||||
let overflow_i64: u64 = ::core::i64::MAX as u64 + 1;
|
||||
assert_eq!(RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(), overflow_i64);
|
||||
let overflow_i64: u64 = ::core::i64::MAX as u64 + 1;
|
||||
assert_eq!(
|
||||
RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(),
|
||||
overflow_i64
|
||||
);
|
||||
}
|
||||
|
||||
pub fn parse_wat(source: &str) -> Module {
|
||||
let wasm_binary = wabt::wat2wasm(source).expect("Failed to parse wat source");
|
||||
Module::from_buffer(wasm_binary).expect("Failed to load parsed module")
|
||||
let wasm_binary = wabt::wat2wasm(source).expect("Failed to parse wat source");
|
||||
Module::from_buffer(wasm_binary).expect("Failed to load parsed module")
|
||||
}
|
||||
|
|
|
@ -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 std::fs::File;
|
||||
use {
|
||||
Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals,
|
||||
RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
|
||||
};
|
||||
|
||||
struct Env {
|
||||
table_base: GlobalRef,
|
||||
memory_base: GlobalRef,
|
||||
memory: MemoryRef,
|
||||
table: TableRef,
|
||||
table_base: GlobalRef,
|
||||
memory_base: GlobalRef,
|
||||
memory: MemoryRef,
|
||||
table: TableRef,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
fn new() -> Env {
|
||||
Env {
|
||||
table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
|
||||
memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
|
||||
memory: MemoryInstance::alloc(Pages(256), None).unwrap(),
|
||||
table: TableInstance::alloc(64, None).unwrap(),
|
||||
}
|
||||
}
|
||||
fn new() -> Env {
|
||||
Env {
|
||||
table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
|
||||
memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
|
||||
memory: MemoryInstance::alloc(Pages(256), None).unwrap(),
|
||||
table: TableInstance::alloc(64, None).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleImportResolver for Env {
|
||||
fn resolve_func(&self, _field_name: &str, _func_type: &Signature) -> Result<FuncRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
"env module doesn't provide any functions".into(),
|
||||
))
|
||||
}
|
||||
fn resolve_func(&self, _field_name: &str, _func_type: &Signature) -> Result<FuncRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
"env module doesn't provide any functions".into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
match field_name {
|
||||
"tableBase" => Ok(self.table_base.clone()),
|
||||
"memoryBase" => Ok(self.memory_base.clone()),
|
||||
_ => Err(Error::Instantiation(format!(
|
||||
"env module doesn't provide global '{}'",
|
||||
field_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
fn resolve_global(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
match field_name {
|
||||
"tableBase" => Ok(self.table_base.clone()),
|
||||
"memoryBase" => Ok(self.memory_base.clone()),
|
||||
_ => Err(Error::Instantiation(format!(
|
||||
"env module doesn't provide global '{}'",
|
||||
field_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
match field_name {
|
||||
"memory" => Ok(self.memory.clone()),
|
||||
_ => Err(Error::Instantiation(format!(
|
||||
"env module doesn't provide memory '{}'",
|
||||
field_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
match field_name {
|
||||
"memory" => Ok(self.memory.clone()),
|
||||
_ => Err(Error::Instantiation(format!(
|
||||
"env module doesn't provide memory '{}'",
|
||||
field_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> {
|
||||
match field_name {
|
||||
"table" => Ok(self.table.clone()),
|
||||
_ => Err(Error::Instantiation(
|
||||
format!("env module doesn't provide table '{}'", field_name),
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> 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 {
|
||||
use std::io::prelude::*;
|
||||
let mut file = File::open(filename).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
let wasm_buf = ::wabt::wat2wasm(&buf).unwrap();
|
||||
Module::from_buffer(wasm_buf).unwrap()
|
||||
use std::io::prelude::*;
|
||||
let mut file = File::open(filename).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
let wasm_buf = ::wabt::wat2wasm(&buf).unwrap();
|
||||
Module::from_buffer(wasm_buf).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpreter_inc_i32() {
|
||||
// Name of function contained in WASM file (note the leading underline)
|
||||
const FUNCTION_NAME: &'static str = "_inc_i32";
|
||||
// The WASM file containing the module and function
|
||||
const WASM_FILE: &str = &"res/fixtures/inc_i32.wast";
|
||||
// Name of function contained in WASM file (note the leading underline)
|
||||
const FUNCTION_NAME: &'static str = "_inc_i32";
|
||||
// The WASM file containing the module and function
|
||||
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(
|
||||
&module,
|
||||
&ImportsBuilder::new().with_resolver("env", &env),
|
||||
).expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let i32_val = 42;
|
||||
// the functions expects a single i32 parameter
|
||||
let args = &[RuntimeValue::I32(i32_val)];
|
||||
let exp_retval = Some(RuntimeValue::I32(i32_val + 1));
|
||||
let i32_val = 42;
|
||||
// the functions expects a single i32 parameter
|
||||
let args = &[RuntimeValue::I32(i32_val)];
|
||||
let exp_retval = Some(RuntimeValue::I32(i32_val + 1));
|
||||
|
||||
let retval = instance
|
||||
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
|
||||
.expect("");
|
||||
assert_eq!(exp_retval, retval);
|
||||
let retval = instance
|
||||
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
|
||||
.expect("");
|
||||
assert_eq!(exp_retval, retval);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -114,18 +117,15 @@ fn interpreter_accumulate_u8() {
|
|||
// The WASM file containing the module and function
|
||||
const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast";
|
||||
// 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
|
||||
let module = load_from_file(WASM_FILE);
|
||||
let module = load_from_file(WASM_FILE);
|
||||
|
||||
let env = Env::new();
|
||||
let instance = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::new().with_resolver("env", &env),
|
||||
).expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
let env = Env::new();
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let env_memory = env.memory.clone();
|
||||
|
||||
|
@ -134,7 +134,10 @@ fn interpreter_accumulate_u8() {
|
|||
let _ = env_memory.set(offset, BUF);
|
||||
|
||||
// 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
|
||||
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
|
||||
.expect("Failed to execute function");
|
||||
|
|
242
src/types.rs
242
src/types.rs
|
@ -1,7 +1,8 @@
|
|||
use alloc::borrow::Cow;
|
||||
|
||||
use parity_wasm::elements::{
|
||||
FunctionType, ValueType as EValueType, GlobalType, TableType, MemoryType};
|
||||
FunctionType, GlobalType, MemoryType, TableType, ValueType as EValueType,
|
||||
};
|
||||
|
||||
/// Signature of a [function].
|
||||
///
|
||||
|
@ -13,55 +14,60 @@ use parity_wasm::elements::{
|
|||
/// [function]: struct.FuncInstance.html
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Signature {
|
||||
params: Cow<'static, [ValueType]>,
|
||||
return_type: Option<ValueType>,
|
||||
params: Cow<'static, [ValueType]>,
|
||||
return_type: Option<ValueType>,
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
/// Creates new signature with givens
|
||||
/// parameter types and optional return type.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use wasmi::{Signature, ValueType};
|
||||
///
|
||||
/// // s1: (i32) -> ()
|
||||
/// let s1 = Signature::new(&[ValueType::I32][..], None);
|
||||
///
|
||||
/// // s2: () -> i32
|
||||
/// let s2 = Signature::new(&[][..], Some(ValueType::I32));
|
||||
///
|
||||
/// // s3: (I64) -> ()
|
||||
/// let dynamic_params = vec![ValueType::I64];
|
||||
/// let s3 = Signature::new(dynamic_params, None);
|
||||
/// ```
|
||||
pub fn new<C: Into<Cow<'static, [ValueType]>>>(
|
||||
params: C,
|
||||
return_type: Option<ValueType>
|
||||
) -> Signature {
|
||||
Signature {
|
||||
params: params.into(),
|
||||
return_type: return_type,
|
||||
}
|
||||
}
|
||||
/// Creates new signature with givens
|
||||
/// parameter types and optional return type.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use wasmi::{Signature, ValueType};
|
||||
///
|
||||
/// // s1: (i32) -> ()
|
||||
/// let s1 = Signature::new(&[ValueType::I32][..], None);
|
||||
///
|
||||
/// // s2: () -> i32
|
||||
/// let s2 = Signature::new(&[][..], Some(ValueType::I32));
|
||||
///
|
||||
/// // s3: (I64) -> ()
|
||||
/// let dynamic_params = vec![ValueType::I64];
|
||||
/// let s3 = Signature::new(dynamic_params, None);
|
||||
/// ```
|
||||
pub fn new<C: Into<Cow<'static, [ValueType]>>>(
|
||||
params: C,
|
||||
return_type: Option<ValueType>,
|
||||
) -> Signature {
|
||||
Signature {
|
||||
params: params.into(),
|
||||
return_type: return_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns parameter types of this signature.
|
||||
pub fn params(&self) -> &[ValueType] {
|
||||
&self.params.as_ref()
|
||||
}
|
||||
/// Returns parameter types of this signature.
|
||||
pub fn params(&self) -> &[ValueType] {
|
||||
&self.params.as_ref()
|
||||
}
|
||||
|
||||
/// Returns return type of this signature.
|
||||
pub fn return_type(&self) -> Option<ValueType> {
|
||||
self.return_type
|
||||
}
|
||||
/// Returns return type of this signature.
|
||||
pub fn return_type(&self) -> Option<ValueType> {
|
||||
self.return_type
|
||||
}
|
||||
|
||||
pub(crate) fn from_elements(func_type: &FunctionType) -> Signature {
|
||||
Signature {
|
||||
params: func_type.params().iter().cloned().map(ValueType::from_elements).collect(),
|
||||
return_type: func_type.return_type().map(ValueType::from_elements),
|
||||
}
|
||||
}
|
||||
pub(crate) fn from_elements(func_type: &FunctionType) -> Signature {
|
||||
Signature {
|
||||
params: func_type
|
||||
.params()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(ValueType::from_elements)
|
||||
.collect(),
|
||||
return_type: func_type.return_type().map(ValueType::from_elements),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of a value.
|
||||
|
@ -71,34 +77,34 @@ impl Signature {
|
|||
/// [`RuntimeValue`]: enum.RuntimeValue.html
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ValueType {
|
||||
/// 32-bit signed or unsigned integer.
|
||||
I32,
|
||||
/// 64-bit signed or unsigned integer.
|
||||
I64,
|
||||
/// 32-bit IEEE 754-2008 floating point number.
|
||||
F32,
|
||||
/// 64-bit IEEE 754-2008 floating point number.
|
||||
F64,
|
||||
/// 32-bit signed or unsigned integer.
|
||||
I32,
|
||||
/// 64-bit signed or unsigned integer.
|
||||
I64,
|
||||
/// 32-bit IEEE 754-2008 floating point number.
|
||||
F32,
|
||||
/// 64-bit IEEE 754-2008 floating point number.
|
||||
F64,
|
||||
}
|
||||
|
||||
impl ValueType {
|
||||
pub(crate) fn from_elements(value_type: EValueType) -> ValueType {
|
||||
match value_type {
|
||||
EValueType::I32 => ValueType::I32,
|
||||
EValueType::I64 => ValueType::I64,
|
||||
EValueType::F32 => ValueType::F32,
|
||||
EValueType::F64 => ValueType::F64,
|
||||
}
|
||||
}
|
||||
pub(crate) fn from_elements(value_type: EValueType) -> ValueType {
|
||||
match value_type {
|
||||
EValueType::I32 => ValueType::I32,
|
||||
EValueType::I64 => ValueType::I64,
|
||||
EValueType::F32 => ValueType::F32,
|
||||
EValueType::F64 => ValueType::F64,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_elements(self) -> EValueType {
|
||||
match self {
|
||||
ValueType::I32 => EValueType::I32,
|
||||
ValueType::I64 => EValueType::I64,
|
||||
ValueType::F32 => EValueType::F32,
|
||||
ValueType::F64 => EValueType::F64,
|
||||
}
|
||||
}
|
||||
pub(crate) fn into_elements(self) -> EValueType {
|
||||
match self {
|
||||
ValueType::I32 => EValueType::I32,
|
||||
ValueType::I64 => EValueType::I64,
|
||||
ValueType::F32 => EValueType::F32,
|
||||
ValueType::F64 => EValueType::F64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of a global variable.
|
||||
|
@ -108,29 +114,29 @@ impl ValueType {
|
|||
///
|
||||
/// [`ImportResolver`]: trait.ImportResolver.html
|
||||
pub struct GlobalDescriptor {
|
||||
value_type: ValueType,
|
||||
mutable: bool,
|
||||
value_type: ValueType,
|
||||
mutable: bool,
|
||||
}
|
||||
|
||||
impl GlobalDescriptor {
|
||||
pub(crate) fn from_elements(global_type: &GlobalType) -> GlobalDescriptor {
|
||||
GlobalDescriptor {
|
||||
value_type: ValueType::from_elements(global_type.content_type()),
|
||||
mutable: global_type.is_mutable(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn from_elements(global_type: &GlobalType) -> GlobalDescriptor {
|
||||
GlobalDescriptor {
|
||||
value_type: ValueType::from_elements(global_type.content_type()),
|
||||
mutable: global_type.is_mutable(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [`ValueType`] of the requested global.
|
||||
///
|
||||
/// [`ValueType`]: enum.ValueType.html
|
||||
pub fn value_type(&self) -> ValueType {
|
||||
self.value_type
|
||||
}
|
||||
/// Returns [`ValueType`] of the requested global.
|
||||
///
|
||||
/// [`ValueType`]: enum.ValueType.html
|
||||
pub fn value_type(&self) -> ValueType {
|
||||
self.value_type
|
||||
}
|
||||
|
||||
/// Returns whether the requested global mutable.
|
||||
pub fn is_mutable(&self) -> bool {
|
||||
self.mutable
|
||||
}
|
||||
/// Returns whether the requested global mutable.
|
||||
pub fn is_mutable(&self) -> bool {
|
||||
self.mutable
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of a table.
|
||||
|
@ -140,27 +146,27 @@ impl GlobalDescriptor {
|
|||
///
|
||||
/// [`ImportResolver`]: trait.ImportResolver.html
|
||||
pub struct TableDescriptor {
|
||||
initial: u32,
|
||||
maximum: Option<u32>,
|
||||
initial: u32,
|
||||
maximum: Option<u32>,
|
||||
}
|
||||
|
||||
impl TableDescriptor {
|
||||
pub(crate) fn from_elements(table_type: &TableType) -> TableDescriptor {
|
||||
TableDescriptor {
|
||||
initial: table_type.limits().initial(),
|
||||
maximum: table_type.limits().maximum(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn from_elements(table_type: &TableType) -> TableDescriptor {
|
||||
TableDescriptor {
|
||||
initial: table_type.limits().initial(),
|
||||
maximum: table_type.limits().maximum(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns initial size of the requested table.
|
||||
pub fn initial(&self) -> u32 {
|
||||
self.initial
|
||||
}
|
||||
/// Returns initial size of the requested table.
|
||||
pub fn initial(&self) -> u32 {
|
||||
self.initial
|
||||
}
|
||||
|
||||
/// Returns maximum size of the requested table.
|
||||
pub fn maximum(&self) -> Option<u32> {
|
||||
self.maximum
|
||||
}
|
||||
/// Returns maximum size of the requested table.
|
||||
pub fn maximum(&self) -> Option<u32> {
|
||||
self.maximum
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of a linear memory.
|
||||
|
@ -170,25 +176,25 @@ impl TableDescriptor {
|
|||
///
|
||||
/// [`ImportResolver`]: trait.ImportResolver.html
|
||||
pub struct MemoryDescriptor {
|
||||
initial: u32,
|
||||
maximum: Option<u32>,
|
||||
initial: u32,
|
||||
maximum: Option<u32>,
|
||||
}
|
||||
|
||||
impl MemoryDescriptor {
|
||||
pub(crate) fn from_elements(memory_type: &MemoryType) -> MemoryDescriptor {
|
||||
MemoryDescriptor {
|
||||
initial: memory_type.limits().initial(),
|
||||
maximum: memory_type.limits().maximum(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn from_elements(memory_type: &MemoryType) -> MemoryDescriptor {
|
||||
MemoryDescriptor {
|
||||
initial: memory_type.limits().initial(),
|
||||
maximum: memory_type.limits().maximum(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns initial size (in pages) of the requested memory.
|
||||
pub fn initial(&self) -> u32 {
|
||||
self.initial
|
||||
}
|
||||
/// Returns initial size (in pages) of the requested memory.
|
||||
pub fn initial(&self) -> u32 {
|
||||
self.initial
|
||||
}
|
||||
|
||||
/// Returns maximum size (in pages) of the requested memory.
|
||||
pub fn maximum(&self) -> Option<u32> {
|
||||
self.maximum
|
||||
}
|
||||
/// Returns maximum size (in pages) of the requested memory.
|
||||
pub fn maximum(&self) -> Option<u32> {
|
||||
self.maximum
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,136 +1,142 @@
|
|||
#[allow(unused_imports)]
|
||||
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;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ModuleContext {
|
||||
pub memories: Vec<MemoryType>,
|
||||
pub tables: Vec<TableType>,
|
||||
pub globals: Vec<GlobalType>,
|
||||
pub types: Vec<FunctionType>,
|
||||
pub func_type_indexes: Vec<u32>,
|
||||
pub memories: Vec<MemoryType>,
|
||||
pub tables: Vec<TableType>,
|
||||
pub globals: Vec<GlobalType>,
|
||||
pub types: Vec<FunctionType>,
|
||||
pub func_type_indexes: Vec<u32>,
|
||||
}
|
||||
|
||||
impl ModuleContext {
|
||||
pub fn memories(&self) -> &[MemoryType] {
|
||||
&self.memories
|
||||
}
|
||||
pub fn memories(&self) -> &[MemoryType] {
|
||||
&self.memories
|
||||
}
|
||||
|
||||
pub fn tables(&self) -> &[TableType] {
|
||||
&self.tables
|
||||
}
|
||||
pub fn tables(&self) -> &[TableType] {
|
||||
&self.tables
|
||||
}
|
||||
|
||||
pub fn globals(&self) -> &[GlobalType] {
|
||||
&self.globals
|
||||
}
|
||||
pub fn globals(&self) -> &[GlobalType] {
|
||||
&self.globals
|
||||
}
|
||||
|
||||
pub fn types(&self) -> &[FunctionType] {
|
||||
&self.types
|
||||
}
|
||||
pub fn types(&self) -> &[FunctionType] {
|
||||
&self.types
|
||||
}
|
||||
|
||||
pub fn func_type_indexes(&self) -> &[u32] {
|
||||
&self.func_type_indexes
|
||||
}
|
||||
pub fn func_type_indexes(&self) -> &[u32] {
|
||||
&self.func_type_indexes
|
||||
}
|
||||
|
||||
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
|
||||
if self.memories().get(idx as usize).is_none() {
|
||||
return Err(Error(format!("Memory at index {} doesn't exists", idx)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
|
||||
if self.memories().get(idx as usize).is_none() {
|
||||
return Err(Error(format!("Memory at index {} doesn't exists", idx)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> {
|
||||
self.tables()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx)))
|
||||
}
|
||||
pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> {
|
||||
self.tables()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx)))
|
||||
}
|
||||
|
||||
pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
|
||||
let ty_idx = self.func_type_indexes()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?;
|
||||
self.require_function_type(*ty_idx)
|
||||
}
|
||||
pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
|
||||
let ty_idx = self
|
||||
.func_type_indexes()
|
||||
.get(idx as usize)
|
||||
.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> {
|
||||
let ty = self.types()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?;
|
||||
pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
|
||||
let ty = self
|
||||
.types()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?;
|
||||
|
||||
let params = ty.params();
|
||||
let return_ty = ty.return_type()
|
||||
.map(BlockType::Value)
|
||||
.unwrap_or(BlockType::NoResult);
|
||||
Ok((params, return_ty))
|
||||
}
|
||||
let params = ty.params();
|
||||
let return_ty = ty
|
||||
.return_type()
|
||||
.map(BlockType::Value)
|
||||
.unwrap_or(BlockType::NoResult);
|
||||
Ok((params, return_ty))
|
||||
}
|
||||
|
||||
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<&GlobalType, Error> {
|
||||
let global = self.globals()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?;
|
||||
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<&GlobalType, Error> {
|
||||
let global = self
|
||||
.globals()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?;
|
||||
|
||||
if let Some(expected_mutable) = mutability {
|
||||
if expected_mutable && !global.is_mutable() {
|
||||
return Err(Error(format!("Expected global {} to be mutable", idx)));
|
||||
}
|
||||
if !expected_mutable && global.is_mutable() {
|
||||
return Err(Error(format!("Expected global {} to be immutable", idx)));
|
||||
}
|
||||
}
|
||||
Ok(global)
|
||||
}
|
||||
if let Some(expected_mutable) = mutability {
|
||||
if expected_mutable && !global.is_mutable() {
|
||||
return Err(Error(format!("Expected global {} to be mutable", idx)));
|
||||
}
|
||||
if !expected_mutable && global.is_mutable() {
|
||||
return Err(Error(format!("Expected global {} to be immutable", idx)));
|
||||
}
|
||||
}
|
||||
Ok(global)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ModuleContextBuilder {
|
||||
memories: Vec<MemoryType>,
|
||||
tables: Vec<TableType>,
|
||||
globals: Vec<GlobalType>,
|
||||
types: Vec<FunctionType>,
|
||||
func_type_indexes: Vec<u32>,
|
||||
memories: Vec<MemoryType>,
|
||||
tables: Vec<TableType>,
|
||||
globals: Vec<GlobalType>,
|
||||
types: Vec<FunctionType>,
|
||||
func_type_indexes: Vec<u32>,
|
||||
}
|
||||
|
||||
impl ModuleContextBuilder {
|
||||
pub fn new() -> ModuleContextBuilder {
|
||||
ModuleContextBuilder::default()
|
||||
}
|
||||
pub fn new() -> ModuleContextBuilder {
|
||||
ModuleContextBuilder::default()
|
||||
}
|
||||
|
||||
pub fn push_memory(&mut self, memory: MemoryType) {
|
||||
self.memories.push(memory);
|
||||
}
|
||||
pub fn push_memory(&mut self, memory: MemoryType) {
|
||||
self.memories.push(memory);
|
||||
}
|
||||
|
||||
pub fn push_table(&mut self, table: TableType) {
|
||||
self.tables.push(table);
|
||||
}
|
||||
pub fn push_table(&mut self, table: TableType) {
|
||||
self.tables.push(table);
|
||||
}
|
||||
|
||||
pub fn push_global(&mut self, global: GlobalType) {
|
||||
self.globals.push(global);
|
||||
}
|
||||
pub fn push_global(&mut self, global: GlobalType) {
|
||||
self.globals.push(global);
|
||||
}
|
||||
|
||||
pub fn set_types(&mut self, types: Vec<FunctionType>) {
|
||||
self.types = types;
|
||||
}
|
||||
pub fn set_types(&mut self, types: Vec<FunctionType>) {
|
||||
self.types = types;
|
||||
}
|
||||
|
||||
pub fn push_func_type_index(&mut self, func_type_index: u32) {
|
||||
self.func_type_indexes.push(func_type_index);
|
||||
}
|
||||
pub fn push_func_type_index(&mut self, func_type_index: u32) {
|
||||
self.func_type_indexes.push(func_type_index);
|
||||
}
|
||||
|
||||
pub fn build(self) -> ModuleContext {
|
||||
let ModuleContextBuilder {
|
||||
memories,
|
||||
tables,
|
||||
globals,
|
||||
types,
|
||||
func_type_indexes,
|
||||
} = self;
|
||||
pub fn build(self) -> ModuleContext {
|
||||
let ModuleContextBuilder {
|
||||
memories,
|
||||
tables,
|
||||
globals,
|
||||
types,
|
||||
func_type_indexes,
|
||||
} = self;
|
||||
|
||||
ModuleContext {
|
||||
memories,
|
||||
tables,
|
||||
globals,
|
||||
types,
|
||||
func_type_indexes,
|
||||
}
|
||||
}
|
||||
ModuleContext {
|
||||
memories,
|
||||
tables,
|
||||
globals,
|
||||
types,
|
||||
func_type_indexes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +1,23 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use core::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet;
|
||||
#[cfg(not(feature = "std"))]
|
||||
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::func::FunctionReader;
|
||||
use memory_units::Pages;
|
||||
use common::stack;
|
||||
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 func;
|
||||
|
@ -30,432 +30,427 @@ mod tests;
|
|||
pub struct Error(String);
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
fn description(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<stack::Error> for Error {
|
||||
fn from(e: stack::Error) -> Error {
|
||||
Error(format!("Stack: {}", e))
|
||||
}
|
||||
fn from(e: stack::Error) -> Error {
|
||||
Error(format!("Stack: {}", e))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ValidatedModule {
|
||||
pub code_map: Vec<isa::Instructions>,
|
||||
pub module: Module,
|
||||
pub code_map: Vec<isa::Instructions>,
|
||||
pub module: Module,
|
||||
}
|
||||
|
||||
impl ::core::ops::Deref for ValidatedModule {
|
||||
type Target = Module;
|
||||
fn deref(&self) -> &Module {
|
||||
&self.module
|
||||
}
|
||||
type Target = Module;
|
||||
fn deref(&self) -> &Module {
|
||||
&self.module
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deny_floating_point(module: &Module) -> Result<(), Error> {
|
||||
if let Some(code) = module.code_section() {
|
||||
for op in code.bodies().iter().flat_map(|body| body.code().elements()) {
|
||||
use parity_wasm::elements::Instruction::*;
|
||||
if let Some(code) = module.code_section() {
|
||||
for op in code.bodies().iter().flat_map(|body| body.code().elements()) {
|
||||
use parity_wasm::elements::Instruction::*;
|
||||
|
||||
macro_rules! match_eq {
|
||||
($pattern:pat) => {
|
||||
|val| if let $pattern = *val { true } else { false }
|
||||
};
|
||||
}
|
||||
macro_rules! match_eq {
|
||||
($pattern:pat) => {
|
||||
|val| if let $pattern = *val { true } else { false }
|
||||
};
|
||||
}
|
||||
|
||||
const DENIED: &[fn(&Instruction) -> bool] = &[
|
||||
match_eq!(F32Load(_, _)),
|
||||
match_eq!(F64Load(_, _)),
|
||||
match_eq!(F32Store(_, _)),
|
||||
match_eq!(F64Store(_, _)),
|
||||
match_eq!(F32Const(_)),
|
||||
match_eq!(F64Const(_)),
|
||||
match_eq!(F32Eq),
|
||||
match_eq!(F32Ne),
|
||||
match_eq!(F32Lt),
|
||||
match_eq!(F32Gt),
|
||||
match_eq!(F32Le),
|
||||
match_eq!(F32Ge),
|
||||
match_eq!(F64Eq),
|
||||
match_eq!(F64Ne),
|
||||
match_eq!(F64Lt),
|
||||
match_eq!(F64Gt),
|
||||
match_eq!(F64Le),
|
||||
match_eq!(F64Ge),
|
||||
match_eq!(F32Abs),
|
||||
match_eq!(F32Neg),
|
||||
match_eq!(F32Ceil),
|
||||
match_eq!(F32Floor),
|
||||
match_eq!(F32Trunc),
|
||||
match_eq!(F32Nearest),
|
||||
match_eq!(F32Sqrt),
|
||||
match_eq!(F32Add),
|
||||
match_eq!(F32Sub),
|
||||
match_eq!(F32Mul),
|
||||
match_eq!(F32Div),
|
||||
match_eq!(F32Min),
|
||||
match_eq!(F32Max),
|
||||
match_eq!(F32Copysign),
|
||||
match_eq!(F64Abs),
|
||||
match_eq!(F64Neg),
|
||||
match_eq!(F64Ceil),
|
||||
match_eq!(F64Floor),
|
||||
match_eq!(F64Trunc),
|
||||
match_eq!(F64Nearest),
|
||||
match_eq!(F64Sqrt),
|
||||
match_eq!(F64Add),
|
||||
match_eq!(F64Sub),
|
||||
match_eq!(F64Mul),
|
||||
match_eq!(F64Div),
|
||||
match_eq!(F64Min),
|
||||
match_eq!(F64Max),
|
||||
match_eq!(F64Copysign),
|
||||
match_eq!(F32ConvertSI32),
|
||||
match_eq!(F32ConvertUI32),
|
||||
match_eq!(F32ConvertSI64),
|
||||
match_eq!(F32ConvertUI64),
|
||||
match_eq!(F32DemoteF64),
|
||||
match_eq!(F64ConvertSI32),
|
||||
match_eq!(F64ConvertUI32),
|
||||
match_eq!(F64ConvertSI64),
|
||||
match_eq!(F64ConvertUI64),
|
||||
match_eq!(F64PromoteF32),
|
||||
match_eq!(F32ReinterpretI32),
|
||||
match_eq!(F64ReinterpretI64),
|
||||
match_eq!(I32TruncSF32),
|
||||
match_eq!(I32TruncUF32),
|
||||
match_eq!(I32TruncSF64),
|
||||
match_eq!(I32TruncUF64),
|
||||
match_eq!(I64TruncSF32),
|
||||
match_eq!(I64TruncUF32),
|
||||
match_eq!(I64TruncSF64),
|
||||
match_eq!(I64TruncUF64),
|
||||
match_eq!(I32ReinterpretF32),
|
||||
match_eq!(I64ReinterpretF64),
|
||||
];
|
||||
const DENIED: &[fn(&Instruction) -> bool] = &[
|
||||
match_eq!(F32Load(_, _)),
|
||||
match_eq!(F64Load(_, _)),
|
||||
match_eq!(F32Store(_, _)),
|
||||
match_eq!(F64Store(_, _)),
|
||||
match_eq!(F32Const(_)),
|
||||
match_eq!(F64Const(_)),
|
||||
match_eq!(F32Eq),
|
||||
match_eq!(F32Ne),
|
||||
match_eq!(F32Lt),
|
||||
match_eq!(F32Gt),
|
||||
match_eq!(F32Le),
|
||||
match_eq!(F32Ge),
|
||||
match_eq!(F64Eq),
|
||||
match_eq!(F64Ne),
|
||||
match_eq!(F64Lt),
|
||||
match_eq!(F64Gt),
|
||||
match_eq!(F64Le),
|
||||
match_eq!(F64Ge),
|
||||
match_eq!(F32Abs),
|
||||
match_eq!(F32Neg),
|
||||
match_eq!(F32Ceil),
|
||||
match_eq!(F32Floor),
|
||||
match_eq!(F32Trunc),
|
||||
match_eq!(F32Nearest),
|
||||
match_eq!(F32Sqrt),
|
||||
match_eq!(F32Add),
|
||||
match_eq!(F32Sub),
|
||||
match_eq!(F32Mul),
|
||||
match_eq!(F32Div),
|
||||
match_eq!(F32Min),
|
||||
match_eq!(F32Max),
|
||||
match_eq!(F32Copysign),
|
||||
match_eq!(F64Abs),
|
||||
match_eq!(F64Neg),
|
||||
match_eq!(F64Ceil),
|
||||
match_eq!(F64Floor),
|
||||
match_eq!(F64Trunc),
|
||||
match_eq!(F64Nearest),
|
||||
match_eq!(F64Sqrt),
|
||||
match_eq!(F64Add),
|
||||
match_eq!(F64Sub),
|
||||
match_eq!(F64Mul),
|
||||
match_eq!(F64Div),
|
||||
match_eq!(F64Min),
|
||||
match_eq!(F64Max),
|
||||
match_eq!(F64Copysign),
|
||||
match_eq!(F32ConvertSI32),
|
||||
match_eq!(F32ConvertUI32),
|
||||
match_eq!(F32ConvertSI64),
|
||||
match_eq!(F32ConvertUI64),
|
||||
match_eq!(F32DemoteF64),
|
||||
match_eq!(F64ConvertSI32),
|
||||
match_eq!(F64ConvertUI32),
|
||||
match_eq!(F64ConvertSI64),
|
||||
match_eq!(F64ConvertUI64),
|
||||
match_eq!(F64PromoteF32),
|
||||
match_eq!(F32ReinterpretI32),
|
||||
match_eq!(F64ReinterpretI64),
|
||||
match_eq!(I32TruncSF32),
|
||||
match_eq!(I32TruncUF32),
|
||||
match_eq!(I32TruncSF64),
|
||||
match_eq!(I32TruncUF64),
|
||||
match_eq!(I64TruncSF32),
|
||||
match_eq!(I64TruncUF32),
|
||||
match_eq!(I64TruncSF64),
|
||||
match_eq!(I64TruncUF64),
|
||||
match_eq!(I32ReinterpretF32),
|
||||
match_eq!(I64ReinterpretF64),
|
||||
];
|
||||
|
||||
if DENIED.iter().any(|is_denied| is_denied(op)) {
|
||||
return Err(Error(format!("Floating point operation denied: {:?}", op)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if DENIED.iter().any(|is_denied| is_denied(op)) {
|
||||
return Err(Error(format!("Floating point operation denied: {:?}", op)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(sec), Some(types)) = (module.function_section(), module.type_section()) {
|
||||
use parity_wasm::elements::{Type, ValueType};
|
||||
if let (Some(sec), Some(types)) = (module.function_section(), module.type_section()) {
|
||||
use parity_wasm::elements::{Type, ValueType};
|
||||
|
||||
let types = types.types();
|
||||
let types = types.types();
|
||||
|
||||
for sig in sec.entries() {
|
||||
if let Some(typ) = types.get(sig.type_ref() as usize) {
|
||||
match *typ {
|
||||
Type::Function(ref func) => {
|
||||
if func.params()
|
||||
.iter()
|
||||
.chain(func.return_type().as_ref())
|
||||
.any(|&typ| typ == ValueType::F32 || typ == ValueType::F64)
|
||||
{
|
||||
return Err(Error(format!("Use of floating point types denied")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for sig in sec.entries() {
|
||||
if let Some(typ) = types.get(sig.type_ref() as usize) {
|
||||
match *typ {
|
||||
Type::Function(ref func) => {
|
||||
if func
|
||||
.params()
|
||||
.iter()
|
||||
.chain(func.return_type().as_ref())
|
||||
.any(|&typ| typ == ValueType::F32 || typ == ValueType::F64)
|
||||
{
|
||||
return Err(Error(format!("Use of floating point types denied")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
||||
let mut context_builder = ModuleContextBuilder::new();
|
||||
let mut imported_globals = Vec::new();
|
||||
let mut code_map = Vec::new();
|
||||
let mut context_builder = ModuleContextBuilder::new();
|
||||
let mut imported_globals = Vec::new();
|
||||
let mut code_map = Vec::new();
|
||||
|
||||
// Copy types from module as is.
|
||||
context_builder.set_types(
|
||||
module
|
||||
.type_section()
|
||||
.map(|ts| {
|
||||
ts.types()
|
||||
.into_iter()
|
||||
.map(|&Type::Function(ref ty)| ty)
|
||||
.cloned()
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
// Copy types from module as is.
|
||||
context_builder.set_types(
|
||||
module
|
||||
.type_section()
|
||||
.map(|ts| {
|
||||
ts.types()
|
||||
.into_iter()
|
||||
.map(|&Type::Function(ref ty)| ty)
|
||||
.cloned()
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
// Fill elements with imported values.
|
||||
for import_entry in module
|
||||
.import_section()
|
||||
.map(|i| i.entries())
|
||||
.unwrap_or_default()
|
||||
{
|
||||
match *import_entry.external() {
|
||||
External::Function(idx) => context_builder.push_func_type_index(idx),
|
||||
External::Table(ref table) => context_builder.push_table(table.clone()),
|
||||
External::Memory(ref memory) => context_builder.push_memory(memory.clone()),
|
||||
External::Global(ref global) => {
|
||||
context_builder.push_global(global.clone());
|
||||
imported_globals.push(global.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fill elements with imported values.
|
||||
for import_entry in module
|
||||
.import_section()
|
||||
.map(|i| i.entries())
|
||||
.unwrap_or_default()
|
||||
{
|
||||
match *import_entry.external() {
|
||||
External::Function(idx) => context_builder.push_func_type_index(idx),
|
||||
External::Table(ref table) => context_builder.push_table(table.clone()),
|
||||
External::Memory(ref memory) => context_builder.push_memory(memory.clone()),
|
||||
External::Global(ref global) => {
|
||||
context_builder.push_global(global.clone());
|
||||
imported_globals.push(global.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Concatenate elements with defined in the module.
|
||||
if let Some(function_section) = module.function_section() {
|
||||
for func_entry in function_section.entries() {
|
||||
context_builder.push_func_type_index(func_entry.type_ref())
|
||||
}
|
||||
}
|
||||
if let Some(table_section) = module.table_section() {
|
||||
for table_entry in table_section.entries() {
|
||||
validate_table_type(table_entry)?;
|
||||
context_builder.push_table(table_entry.clone());
|
||||
}
|
||||
}
|
||||
if let Some(mem_section) = module.memory_section() {
|
||||
for mem_entry in mem_section.entries() {
|
||||
validate_memory_type(mem_entry)?;
|
||||
context_builder.push_memory(mem_entry.clone());
|
||||
}
|
||||
}
|
||||
if let Some(global_section) = module.global_section() {
|
||||
for global_entry in global_section.entries() {
|
||||
validate_global_entry(global_entry, &imported_globals)?;
|
||||
context_builder.push_global(global_entry.global_type().clone());
|
||||
}
|
||||
}
|
||||
// Concatenate elements with defined in the module.
|
||||
if let Some(function_section) = module.function_section() {
|
||||
for func_entry in function_section.entries() {
|
||||
context_builder.push_func_type_index(func_entry.type_ref())
|
||||
}
|
||||
}
|
||||
if let Some(table_section) = module.table_section() {
|
||||
for table_entry in table_section.entries() {
|
||||
validate_table_type(table_entry)?;
|
||||
context_builder.push_table(table_entry.clone());
|
||||
}
|
||||
}
|
||||
if let Some(mem_section) = module.memory_section() {
|
||||
for mem_entry in mem_section.entries() {
|
||||
validate_memory_type(mem_entry)?;
|
||||
context_builder.push_memory(mem_entry.clone());
|
||||
}
|
||||
}
|
||||
if let Some(global_section) = module.global_section() {
|
||||
for global_entry in global_section.entries() {
|
||||
validate_global_entry(global_entry, &imported_globals)?;
|
||||
context_builder.push_global(global_entry.global_type().clone());
|
||||
}
|
||||
}
|
||||
|
||||
let context = context_builder.build();
|
||||
let context = context_builder.build();
|
||||
|
||||
let function_section_len = module
|
||||
.function_section()
|
||||
.map(|s| s.entries().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 {
|
||||
return Err(Error(format!(
|
||||
"length of function section is {}, while len of code section is {}",
|
||||
function_section_len,
|
||||
code_section_len
|
||||
)));
|
||||
}
|
||||
let function_section_len = module
|
||||
.function_section()
|
||||
.map(|s| s.entries().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 {
|
||||
return Err(Error(format!(
|
||||
"length of function section is {}, while len of code section is {}",
|
||||
function_section_len, code_section_len
|
||||
)));
|
||||
}
|
||||
|
||||
// validate every function body in user modules
|
||||
if function_section_len != 0 {
|
||||
// tests use invalid code
|
||||
let function_section = module.function_section().expect(
|
||||
"function_section_len != 0; qed",
|
||||
);
|
||||
let code_section = module.code_section().expect(
|
||||
"function_section_len != 0; function_section_len == code_section_len; qed",
|
||||
);
|
||||
// check every function body
|
||||
for (index, function) in function_section.entries().iter().enumerate() {
|
||||
let function_body = code_section.bodies().get(index as usize).ok_or(
|
||||
Error(format!(
|
||||
"Missing body for function {}",
|
||||
index
|
||||
)),
|
||||
)?;
|
||||
let code = FunctionReader::read_function(&context, function, function_body)
|
||||
.map_err(|e| {
|
||||
let Error(ref msg) = e;
|
||||
Error(format!("Function #{} reading/validation error: {}", index, msg))
|
||||
})?;
|
||||
code_map.push(code);
|
||||
}
|
||||
}
|
||||
// validate every function body in user modules
|
||||
if function_section_len != 0 {
|
||||
// tests use invalid code
|
||||
let function_section = module
|
||||
.function_section()
|
||||
.expect("function_section_len != 0; qed");
|
||||
let code_section = module
|
||||
.code_section()
|
||||
.expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
||||
// check every function body
|
||||
for (index, function) in function_section.entries().iter().enumerate() {
|
||||
let function_body = code_section
|
||||
.bodies()
|
||||
.get(index as usize)
|
||||
.ok_or(Error(format!("Missing body for function {}", index)))?;
|
||||
let code =
|
||||
FunctionReader::read_function(&context, function, function_body).map_err(|e| {
|
||||
let Error(ref msg) = e;
|
||||
Error(format!(
|
||||
"Function #{} reading/validation error: {}",
|
||||
index, msg
|
||||
))
|
||||
})?;
|
||||
code_map.push(code);
|
||||
}
|
||||
}
|
||||
|
||||
// validate start section
|
||||
if let Some(start_fn_idx) = module.start_section() {
|
||||
let (params, return_ty) = context.require_function(start_fn_idx)?;
|
||||
if return_ty != BlockType::NoResult || params.len() != 0 {
|
||||
return Err(Error(
|
||||
"start function expected to have type [] -> []".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// validate start section
|
||||
if let Some(start_fn_idx) = module.start_section() {
|
||||
let (params, return_ty) = context.require_function(start_fn_idx)?;
|
||||
if return_ty != BlockType::NoResult || params.len() != 0 {
|
||||
return Err(Error(
|
||||
"start function expected to have type [] -> []".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// validate export section
|
||||
if let Some(export_section) = module.export_section() {
|
||||
let mut export_names = HashSet::with_capacity(export_section.entries().len());
|
||||
for export in export_section.entries() {
|
||||
// HashSet::insert returns false if item already in set.
|
||||
let duplicate = export_names.insert(export.field()) == false;
|
||||
if duplicate {
|
||||
return Err(Error(
|
||||
format!("duplicate export {}", export.field()),
|
||||
));
|
||||
}
|
||||
match *export.internal() {
|
||||
Internal::Function(function_index) => {
|
||||
context.require_function(function_index)?;
|
||||
}
|
||||
Internal::Global(global_index) => {
|
||||
context.require_global(global_index, Some(false))?;
|
||||
}
|
||||
Internal::Memory(memory_index) => {
|
||||
context.require_memory(memory_index)?;
|
||||
}
|
||||
Internal::Table(table_index) => {
|
||||
context.require_table(table_index)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// validate export section
|
||||
if let Some(export_section) = module.export_section() {
|
||||
let mut export_names = HashSet::with_capacity(export_section.entries().len());
|
||||
for export in export_section.entries() {
|
||||
// HashSet::insert returns false if item already in set.
|
||||
let duplicate = export_names.insert(export.field()) == false;
|
||||
if duplicate {
|
||||
return Err(Error(format!("duplicate export {}", export.field())));
|
||||
}
|
||||
match *export.internal() {
|
||||
Internal::Function(function_index) => {
|
||||
context.require_function(function_index)?;
|
||||
}
|
||||
Internal::Global(global_index) => {
|
||||
context.require_global(global_index, Some(false))?;
|
||||
}
|
||||
Internal::Memory(memory_index) => {
|
||||
context.require_memory(memory_index)?;
|
||||
}
|
||||
Internal::Table(table_index) => {
|
||||
context.require_table(table_index)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate import section
|
||||
if let Some(import_section) = module.import_section() {
|
||||
for import in import_section.entries() {
|
||||
match *import.external() {
|
||||
External::Function(function_type_index) => {
|
||||
context.require_function_type(function_type_index)?;
|
||||
}
|
||||
External::Global(ref global_type) => {
|
||||
if global_type.is_mutable() {
|
||||
return Err(Error(format!(
|
||||
"trying to import mutable global {}",
|
||||
import.field()
|
||||
)));
|
||||
}
|
||||
}
|
||||
External::Memory(ref memory_type) => {
|
||||
validate_memory_type(memory_type)?;
|
||||
}
|
||||
External::Table(ref table_type) => {
|
||||
validate_table_type(table_type)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// validate import section
|
||||
if let Some(import_section) = module.import_section() {
|
||||
for import in import_section.entries() {
|
||||
match *import.external() {
|
||||
External::Function(function_type_index) => {
|
||||
context.require_function_type(function_type_index)?;
|
||||
}
|
||||
External::Global(ref global_type) => {
|
||||
if global_type.is_mutable() {
|
||||
return Err(Error(format!(
|
||||
"trying to import mutable global {}",
|
||||
import.field()
|
||||
)));
|
||||
}
|
||||
}
|
||||
External::Memory(ref memory_type) => {
|
||||
validate_memory_type(memory_type)?;
|
||||
}
|
||||
External::Table(ref table_type) => {
|
||||
validate_table_type(table_type)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// there must be no greater than 1 table in tables index space
|
||||
if context.tables().len() > 1 {
|
||||
return Err(Error(format!(
|
||||
"too many tables in index space: {}",
|
||||
context.tables().len()
|
||||
)));
|
||||
}
|
||||
// there must be no greater than 1 table in tables index space
|
||||
if context.tables().len() > 1 {
|
||||
return Err(Error(format!(
|
||||
"too many tables in index space: {}",
|
||||
context.tables().len()
|
||||
)));
|
||||
}
|
||||
|
||||
// there must be no greater than 1 linear memory in memory index space
|
||||
if context.memories().len() > 1 {
|
||||
return Err(Error(format!(
|
||||
"too many memory regions in index space: {}",
|
||||
context.memories().len()
|
||||
)));
|
||||
}
|
||||
// there must be no greater than 1 linear memory in memory index space
|
||||
if context.memories().len() > 1 {
|
||||
return Err(Error(format!(
|
||||
"too many memory regions in index space: {}",
|
||||
context.memories().len()
|
||||
)));
|
||||
}
|
||||
|
||||
// use data section to initialize linear memory regions
|
||||
if let Some(data_section) = module.data_section() {
|
||||
for data_segment in data_section.entries() {
|
||||
context.require_memory(data_segment.index())?;
|
||||
let init_ty = expr_const_type(data_segment.offset(), context.globals())?;
|
||||
if init_ty != ValueType::I32 {
|
||||
return Err(Error("segment offset should return I32".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// use data section to initialize linear memory regions
|
||||
if let Some(data_section) = module.data_section() {
|
||||
for data_segment in data_section.entries() {
|
||||
context.require_memory(data_segment.index())?;
|
||||
let init_ty = expr_const_type(data_segment.offset(), context.globals())?;
|
||||
if init_ty != ValueType::I32 {
|
||||
return Err(Error("segment offset should return I32".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use element section to fill tables
|
||||
if let Some(element_section) = module.elements_section() {
|
||||
for element_segment in element_section.entries() {
|
||||
context.require_table(element_segment.index())?;
|
||||
// use element section to fill tables
|
||||
if let Some(element_section) = module.elements_section() {
|
||||
for element_segment in element_section.entries() {
|
||||
context.require_table(element_segment.index())?;
|
||||
|
||||
let init_ty = expr_const_type(element_segment.offset(), context.globals())?;
|
||||
if init_ty != ValueType::I32 {
|
||||
return Err(Error("segment offset should return I32".into()));
|
||||
}
|
||||
let init_ty = expr_const_type(element_segment.offset(), context.globals())?;
|
||||
if init_ty != ValueType::I32 {
|
||||
return Err(Error("segment offset should return I32".into()));
|
||||
}
|
||||
|
||||
for function_index in element_segment.members() {
|
||||
context.require_function(*function_index)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
for function_index in element_segment.members() {
|
||||
context.require_function(*function_index)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ValidatedModule {
|
||||
module,
|
||||
code_map,
|
||||
})
|
||||
Ok(ValidatedModule { module, code_map })
|
||||
}
|
||||
|
||||
fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
||||
if let Some(maximum) = limits.maximum() {
|
||||
if limits.initial() > maximum {
|
||||
return Err(Error(format!(
|
||||
"maximum limit {} is less than minimum {}",
|
||||
maximum,
|
||||
limits.initial()
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
if let Some(maximum) = limits.maximum() {
|
||||
if limits.initial() > maximum {
|
||||
return Err(Error(format!(
|
||||
"maximum limit {} is less than minimum {}",
|
||||
maximum,
|
||||
limits.initial()
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> {
|
||||
let initial: Pages = Pages(memory_type.limits().initial() as usize);
|
||||
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
|
||||
::memory::validate_memory(initial, maximum).map_err(Error)
|
||||
let initial: Pages = Pages(memory_type.limits().initial() as usize);
|
||||
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
|
||||
::memory::validate_memory(initial, maximum).map_err(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> {
|
||||
let init = global_entry.init_expr();
|
||||
let init_expr_ty = expr_const_type(init, globals)?;
|
||||
if init_expr_ty != global_entry.global_type().content_type() {
|
||||
return Err(Error(format!(
|
||||
"Trying to initialize variable of type {:?} with value of type {:?}",
|
||||
global_entry.global_type().content_type(),
|
||||
init_expr_ty
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
let init = global_entry.init_expr();
|
||||
let init_expr_ty = expr_const_type(init, globals)?;
|
||||
if init_expr_ty != global_entry.global_type().content_type() {
|
||||
return Err(Error(format!(
|
||||
"Trying to initialize variable of type {:?} with value of type {:?}",
|
||||
global_entry.global_type().content_type(),
|
||||
init_expr_ty
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns type of this constant expression.
|
||||
fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<ValueType, Error> {
|
||||
let code = init_expr.code();
|
||||
if code.len() != 2 {
|
||||
return Err(Error(
|
||||
"Init expression should always be with length 2".into(),
|
||||
));
|
||||
}
|
||||
let expr_ty: ValueType = match code[0] {
|
||||
Instruction::I32Const(_) => ValueType::I32,
|
||||
Instruction::I64Const(_) => ValueType::I64,
|
||||
Instruction::F32Const(_) => ValueType::F32,
|
||||
Instruction::F64Const(_) => ValueType::F64,
|
||||
Instruction::GetGlobal(idx) => {
|
||||
match globals.get(idx as usize) {
|
||||
Some(target_global) => {
|
||||
if target_global.is_mutable() {
|
||||
return Err(Error(format!("Global {} is mutable", idx)));
|
||||
}
|
||||
target_global.content_type()
|
||||
}
|
||||
None => {
|
||||
return Err(Error(
|
||||
format!("Global {} doesn't exists or not yet defined", idx),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return Err(Error("Non constant opcode in init expr".into())),
|
||||
};
|
||||
if code[1] != Instruction::End {
|
||||
return Err(Error("Expression doesn't ends with `end` opcode".into()));
|
||||
}
|
||||
Ok(expr_ty)
|
||||
let code = init_expr.code();
|
||||
if code.len() != 2 {
|
||||
return Err(Error(
|
||||
"Init expression should always be with length 2".into(),
|
||||
));
|
||||
}
|
||||
let expr_ty: ValueType = match code[0] {
|
||||
Instruction::I32Const(_) => ValueType::I32,
|
||||
Instruction::I64Const(_) => ValueType::I64,
|
||||
Instruction::F32Const(_) => ValueType::F32,
|
||||
Instruction::F64Const(_) => ValueType::F64,
|
||||
Instruction::GetGlobal(idx) => match globals.get(idx as usize) {
|
||||
Some(target_global) => {
|
||||
if target_global.is_mutable() {
|
||||
return Err(Error(format!("Global {} is mutable", idx)));
|
||||
}
|
||||
target_global.content_type()
|
||||
}
|
||||
None => {
|
||||
return Err(Error(format!(
|
||||
"Global {} doesn't exists or not yet defined",
|
||||
idx
|
||||
)));
|
||||
}
|
||||
},
|
||||
_ => return Err(Error("Non constant opcode in init expr".into())),
|
||||
};
|
||||
if code[1] != Instruction::End {
|
||||
return Err(Error("Expression doesn't ends with `end` opcode".into()));
|
||||
}
|
||||
Ok(expr_ty)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,124 +10,119 @@ use validation::Error;
|
|||
/// of a value_type and a count.
|
||||
#[derive(Debug)]
|
||||
pub struct Locals<'a> {
|
||||
params: &'a [ValueType],
|
||||
local_groups: &'a [Local],
|
||||
count: u32,
|
||||
params: &'a [ValueType],
|
||||
local_groups: &'a [Local],
|
||||
count: u32,
|
||||
}
|
||||
|
||||
impl<'a> Locals<'a> {
|
||||
/// Create a new wrapper around declared variables and parameters.
|
||||
pub fn new(params: &'a [ValueType], local_groups: &'a [Local]) -> Result<Locals<'a>, Error> {
|
||||
let mut acc = params.len() as u32;
|
||||
for locals_group in local_groups {
|
||||
acc = acc
|
||||
.checked_add(locals_group.count())
|
||||
.ok_or_else(||
|
||||
Error(String::from("Locals range not in 32-bit range"))
|
||||
)?;
|
||||
}
|
||||
/// Create a new wrapper around declared variables and parameters.
|
||||
pub fn new(params: &'a [ValueType], local_groups: &'a [Local]) -> Result<Locals<'a>, Error> {
|
||||
let mut acc = params.len() as u32;
|
||||
for locals_group in local_groups {
|
||||
acc = acc
|
||||
.checked_add(locals_group.count())
|
||||
.ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
|
||||
}
|
||||
|
||||
Ok(Locals {
|
||||
params,
|
||||
local_groups,
|
||||
count: acc,
|
||||
})
|
||||
}
|
||||
Ok(Locals {
|
||||
params,
|
||||
local_groups,
|
||||
count: acc,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns parameter count.
|
||||
pub fn param_count(&self) -> u32 {
|
||||
self.params.len() as u32
|
||||
}
|
||||
/// Returns parameter count.
|
||||
pub fn param_count(&self) -> u32 {
|
||||
self.params.len() as u32
|
||||
}
|
||||
|
||||
/// Returns total count of all declared locals and paramaterers.
|
||||
pub fn count(&self) -> u32 {
|
||||
self.count
|
||||
}
|
||||
/// Returns total count of all declared locals and paramaterers.
|
||||
pub fn count(&self) -> u32 {
|
||||
self.count
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn type_of_local(&self, idx: u32) -> Result<ValueType, Error> {
|
||||
if let Some(param) = self.params.get(idx as usize) {
|
||||
return Ok(*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.
|
||||
pub fn type_of_local(&self, idx: u32) -> Result<ValueType, Error> {
|
||||
if let Some(param) = self.params.get(idx as usize) {
|
||||
return Ok(*param);
|
||||
}
|
||||
|
||||
// If an index doesn't point to a param, then we have to look into local declarations.
|
||||
let mut start_idx = self.param_count();
|
||||
for locals_group in self.local_groups {
|
||||
let end_idx = start_idx
|
||||
.checked_add(locals_group.count())
|
||||
.ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
|
||||
// If an index doesn't point to a param, then we have to look into local declarations.
|
||||
let mut start_idx = self.param_count();
|
||||
for locals_group in self.local_groups {
|
||||
let end_idx = start_idx
|
||||
.checked_add(locals_group.count())
|
||||
.ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
|
||||
|
||||
if idx >= start_idx && idx < end_idx {
|
||||
return Ok(locals_group.value_type());
|
||||
}
|
||||
if idx >= start_idx && idx < end_idx {
|
||||
return Ok(locals_group.value_type());
|
||||
}
|
||||
|
||||
start_idx = end_idx;
|
||||
}
|
||||
start_idx = end_idx;
|
||||
}
|
||||
|
||||
// We didn't find anything, that's an error.
|
||||
// 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()`)
|
||||
let total_count = start_idx;
|
||||
// We didn't find anything, that's an error.
|
||||
// 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()`)
|
||||
let total_count = start_idx;
|
||||
|
||||
Err(Error(format!(
|
||||
"Trying to access local with index {} when there are only {} locals",
|
||||
idx, total_count
|
||||
)))
|
||||
}
|
||||
Err(Error(format!(
|
||||
"Trying to access local with index {} when there are only {} locals",
|
||||
idx, total_count
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn locals_it_works() {
|
||||
let params = vec![ValueType::I32, ValueType::I64];
|
||||
let local_groups = vec![Local::new(2, ValueType::F32), Local::new(2, ValueType::F64)];
|
||||
let locals = Locals::new(¶ms, &local_groups).unwrap();
|
||||
#[test]
|
||||
fn locals_it_works() {
|
||||
let params = vec![ValueType::I32, ValueType::I64];
|
||||
let local_groups = vec![Local::new(2, ValueType::F32), Local::new(2, ValueType::F64)];
|
||||
let locals = Locals::new(¶ms, &local_groups).unwrap();
|
||||
|
||||
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(2), 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(5), Ok(ValueType::F64));
|
||||
assert_matches!(locals.type_of_local(6), Err(_));
|
||||
}
|
||||
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(2), 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(5), Ok(ValueType::F64));
|
||||
assert_matches!(locals.type_of_local(6), Err(_));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locals_no_declared_locals() {
|
||||
let params = vec![ValueType::I32];
|
||||
let locals = Locals::new(¶ms, &[]).unwrap();
|
||||
#[test]
|
||||
fn locals_no_declared_locals() {
|
||||
let params = vec![ValueType::I32];
|
||||
let locals = Locals::new(¶ms, &[]).unwrap();
|
||||
|
||||
assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
|
||||
assert_matches!(locals.type_of_local(1), Err(_));
|
||||
}
|
||||
assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
|
||||
assert_matches!(locals.type_of_local(1), Err(_));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locals_no_params() {
|
||||
let local_groups = vec![Local::new(2, ValueType::I32), Local::new(3, ValueType::I64)];
|
||||
let locals = Locals::new(&[], &local_groups).unwrap();
|
||||
#[test]
|
||||
fn locals_no_params() {
|
||||
let local_groups = vec![Local::new(2, ValueType::I32), Local::new(3, ValueType::I64)];
|
||||
let locals = Locals::new(&[], &local_groups).unwrap();
|
||||
|
||||
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(2), 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(5), Err(_));
|
||||
}
|
||||
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(2), 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(5), Err(_));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locals_u32_overflow() {
|
||||
let local_groups = vec![
|
||||
Local::new(u32::max_value(), ValueType::I32),
|
||||
Local::new(1, ValueType::I64),
|
||||
];
|
||||
assert_matches!(
|
||||
Locals::new(&[], &local_groups),
|
||||
Err(_)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn locals_u32_overflow() {
|
||||
let local_groups = vec![
|
||||
Local::new(u32::max_value(), ValueType::I32),
|
||||
Local::new(1, ValueType::I64),
|
||||
];
|
||||
assert_matches!(Locals::new(&[], &local_groups), Err(_));
|
||||
}
|
||||
}
|
||||
|
|
1094
src/value.rs
1094
src/value.rs
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,12 @@
|
|||
mod run;
|
||||
|
||||
macro_rules! run_test {
|
||||
($label: expr, $test_name: ident) => (
|
||||
($label: expr, $test_name: ident) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
self::run::spec($label)
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
run_test!("address", wasm_address);
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
|
||||
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
|
||||
use wasmi::memory_units::Pages;
|
||||
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
|
||||
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap};
|
||||
use wasmi::{
|
||||
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance,
|
||||
GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
|
||||
ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature,
|
||||
TableDescriptor, TableInstance, TableRef, Trap,
|
||||
};
|
||||
|
||||
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
|
||||
match val {
|
||||
Value::I32(v) => RuntimeValue::I32(v),
|
||||
Value::I64(v) => RuntimeValue::I64(v),
|
||||
Value::F32(v) => RuntimeValue::F32(v.into()),
|
||||
Value::F64(v) => RuntimeValue::F64(v.into()),
|
||||
}
|
||||
match val {
|
||||
Value::I32(v) => RuntimeValue::I32(v),
|
||||
Value::I64(v) => RuntimeValue::I64(v),
|
||||
Value::F32(v) => RuntimeValue::F32(v.into()),
|
||||
Value::F64(v) => RuntimeValue::F64(v.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -48,15 +50,15 @@ struct SpecModule {
|
|||
}
|
||||
|
||||
impl SpecModule {
|
||||
fn new() -> Self {
|
||||
SpecModule {
|
||||
table: TableInstance::alloc(10, Some(20)).unwrap(),
|
||||
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
|
||||
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
|
||||
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false),
|
||||
global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false),
|
||||
}
|
||||
}
|
||||
fn new() -> Self {
|
||||
SpecModule {
|
||||
table: TableInstance::alloc(10, Some(20)).unwrap(),
|
||||
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
|
||||
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
|
||||
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false),
|
||||
global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PRINT_FUNC_INDEX: usize = 0;
|
||||
|
@ -83,29 +85,29 @@ impl ModuleImportResolver for SpecModule {
|
|||
field_name: &str,
|
||||
func_type: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
let index = match field_name {
|
||||
"print" => PRINT_FUNC_INDEX,
|
||||
"print_i32" => PRINT_FUNC_INDEX,
|
||||
"print_i32_f32" => PRINT_FUNC_INDEX,
|
||||
"print_f64_f64" => PRINT_FUNC_INDEX,
|
||||
"print_f32" => PRINT_FUNC_INDEX,
|
||||
"print_f64" => PRINT_FUNC_INDEX,
|
||||
_ => {
|
||||
return Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host func import {}",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
};
|
||||
let index = match field_name {
|
||||
"print" => PRINT_FUNC_INDEX,
|
||||
"print_i32" => PRINT_FUNC_INDEX,
|
||||
"print_i32_f32" => PRINT_FUNC_INDEX,
|
||||
"print_f64_f64" => PRINT_FUNC_INDEX,
|
||||
"print_f32" => PRINT_FUNC_INDEX,
|
||||
"print_f64" => PRINT_FUNC_INDEX,
|
||||
_ => {
|
||||
return Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host func import {}",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
if func_type.return_type().is_some() {
|
||||
return Err(InterpreterError::Instantiation(
|
||||
"Function `print_` have unit return type".into(),
|
||||
));
|
||||
}
|
||||
if func_type.return_type().is_some() {
|
||||
return Err(InterpreterError::Instantiation(
|
||||
"Function `print_` have unit return type".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let func = FuncInstance::alloc_host(func_type.clone(), index);
|
||||
return Ok(func);
|
||||
return Ok(func);
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
|
@ -113,380 +115,389 @@ impl ModuleImportResolver for SpecModule {
|
|||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, InterpreterError> {
|
||||
match field_name {
|
||||
"global_i32" => Ok(self.global_i32.clone()),
|
||||
"global_f32" => Ok(self.global_f32.clone()),
|
||||
"global_f64" => Ok(self.global_f64.clone()),
|
||||
_ => Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host global import {}",
|
||||
field_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
match field_name {
|
||||
"global_i32" => Ok(self.global_i32.clone()),
|
||||
"global_f32" => Ok(self.global_f32.clone()),
|
||||
"global_f64" => Ok(self.global_f64.clone()),
|
||||
_ => Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host global import {}",
|
||||
field_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, InterpreterError> {
|
||||
if field_name == "memory" {
|
||||
return Ok(self.memory.clone());
|
||||
}
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, InterpreterError> {
|
||||
if field_name == "memory" {
|
||||
return Ok(self.memory.clone());
|
||||
}
|
||||
|
||||
Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host memory import {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host memory import {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, InterpreterError> {
|
||||
if field_name == "table" {
|
||||
return Ok(self.table.clone());
|
||||
}
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, InterpreterError> {
|
||||
if field_name == "table" {
|
||||
return Ok(self.table.clone());
|
||||
}
|
||||
|
||||
Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host table import {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
Err(InterpreterError::Instantiation(format!(
|
||||
"Unknown host table import {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
struct SpecDriver {
|
||||
spec_module: SpecModule,
|
||||
instances: HashMap<String, ModuleRef>,
|
||||
last_module: Option<ModuleRef>,
|
||||
spec_module: SpecModule,
|
||||
instances: HashMap<String, ModuleRef>,
|
||||
last_module: Option<ModuleRef>,
|
||||
}
|
||||
|
||||
impl SpecDriver {
|
||||
fn new() -> SpecDriver {
|
||||
SpecDriver {
|
||||
spec_module: SpecModule::new(),
|
||||
instances: HashMap::new(),
|
||||
last_module: None,
|
||||
}
|
||||
}
|
||||
fn new() -> SpecDriver {
|
||||
SpecDriver {
|
||||
spec_module: SpecModule::new(),
|
||||
instances: HashMap::new(),
|
||||
last_module: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn spec_module(&mut self) -> &mut SpecModule {
|
||||
&mut self.spec_module
|
||||
}
|
||||
fn spec_module(&mut self) -> &mut SpecModule {
|
||||
&mut self.spec_module
|
||||
}
|
||||
|
||||
fn add_module(&mut self, name: Option<String>, module: ModuleRef) {
|
||||
self.last_module = Some(module.clone());
|
||||
if let Some(name) = name {
|
||||
self.instances.insert(name, module);
|
||||
}
|
||||
}
|
||||
fn add_module(&mut self, name: Option<String>, module: ModuleRef) {
|
||||
self.last_module = Some(module.clone());
|
||||
if let Some(name) = name {
|
||||
self.instances.insert(name, module);
|
||||
}
|
||||
}
|
||||
|
||||
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
|
||||
self.instances.get(name).cloned().ok_or_else(|| {
|
||||
InterpreterError::Instantiation(format!("Module not registered {}", name))
|
||||
})
|
||||
}
|
||||
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
|
||||
self.instances.get(name).cloned().ok_or_else(|| {
|
||||
InterpreterError::Instantiation(format!("Module not registered {}", name))
|
||||
})
|
||||
}
|
||||
|
||||
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
|
||||
match name {
|
||||
Some(name) => self.module(name),
|
||||
None => self.last_module
|
||||
.clone()
|
||||
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
|
||||
}
|
||||
}
|
||||
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
|
||||
match name {
|
||||
Some(name) => self.module(name),
|
||||
None => self
|
||||
.last_module
|
||||
.clone()
|
||||
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportResolver for SpecDriver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
func_type: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_func(field_name, func_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_func(field_name, func_type)
|
||||
}
|
||||
}
|
||||
fn resolve_func(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
func_type: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_func(field_name, func_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_func(field_name, func_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_global(field_name, global_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_global(field_name, global_type)
|
||||
}
|
||||
}
|
||||
fn resolve_global(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_global(field_name, global_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_global(field_name, global_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_memory(field_name, memory_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_memory(field_name, memory_type)
|
||||
}
|
||||
}
|
||||
fn resolve_memory(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_memory(field_name, memory_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_memory(field_name, memory_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_table(field_name, table_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_table(field_name, table_type)
|
||||
}
|
||||
}
|
||||
fn resolve_table(
|
||||
&self,
|
||||
module_name: &str,
|
||||
field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, InterpreterError> {
|
||||
if module_name == "spectest" {
|
||||
self.spec_module.resolve_table(field_name, table_type)
|
||||
} else {
|
||||
self.module(module_name)?
|
||||
.resolve_table(field_name, table_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
let module = try_load_module(wasm)?;
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?;
|
||||
instance
|
||||
.run_start(spec_driver.spec_module())
|
||||
.map_err(|trap| Error::Start(trap))?;
|
||||
Ok(())
|
||||
let module = try_load_module(wasm)?;
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?;
|
||||
instance
|
||||
.run_start(spec_driver.spec_module())
|
||||
.map_err(|trap| Error::Start(trap))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_module(
|
||||
wasm: &[u8],
|
||||
name: &Option<String>,
|
||||
spec_driver: &mut SpecDriver,
|
||||
wasm: &[u8],
|
||||
name: &Option<String>,
|
||||
spec_driver: &mut SpecDriver,
|
||||
) -> Result<ModuleRef, Error> {
|
||||
let module = try_load_module(wasm)?;
|
||||
let instance = ModuleInstance::new(&module, spec_driver)
|
||||
.map_err(|e| Error::Load(e.to_string()))?
|
||||
.run_start(spec_driver.spec_module())
|
||||
.map_err(|trap| Error::Start(trap))?;
|
||||
let module = try_load_module(wasm)?;
|
||||
let instance = ModuleInstance::new(&module, spec_driver)
|
||||
.map_err(|e| Error::Load(e.to_string()))?
|
||||
.run_start(spec_driver.spec_module())
|
||||
.map_err(|trap| Error::Start(trap))?;
|
||||
|
||||
let module_name = name.clone();
|
||||
spec_driver.add_module(module_name, instance.clone());
|
||||
let module_name = name.clone();
|
||||
spec_driver.add_module(module_name, instance.clone());
|
||||
|
||||
Ok(instance)
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
fn run_action(
|
||||
program: &mut SpecDriver,
|
||||
action: &Action<u32, u64>,
|
||||
program: &mut SpecDriver,
|
||||
action: &Action<u32, u64>,
|
||||
) -> Result<Option<RuntimeValue>, InterpreterError> {
|
||||
match *action {
|
||||
Action::Invoke {
|
||||
ref module,
|
||||
ref field,
|
||||
ref args,
|
||||
} => {
|
||||
let module = program
|
||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||
.expect(&format!(
|
||||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let vec_args = args.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
module.invoke_export(field, &vec_args, program.spec_module())
|
||||
}
|
||||
Action::Get {
|
||||
ref module,
|
||||
ref field,
|
||||
..
|
||||
} => {
|
||||
let module = program
|
||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||
.expect(&format!(
|
||||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let global = module
|
||||
.export_by_name(&field)
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected to have export with name {}", field))
|
||||
})?
|
||||
.as_global()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected export {} to be a global", field))
|
||||
})?;
|
||||
Ok(Some(global.get()))
|
||||
}
|
||||
}
|
||||
match *action {
|
||||
Action::Invoke {
|
||||
ref module,
|
||||
ref field,
|
||||
ref args,
|
||||
} => {
|
||||
let module = program
|
||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||
.expect(&format!(
|
||||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let vec_args = args
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
module.invoke_export(field, &vec_args, program.spec_module())
|
||||
}
|
||||
Action::Get {
|
||||
ref module,
|
||||
ref field,
|
||||
..
|
||||
} => {
|
||||
let module = program
|
||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||
.expect(&format!(
|
||||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let global = module
|
||||
.export_by_name(&field)
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected to have export with name {}", field))
|
||||
})?
|
||||
.as_global()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected export {} to be a global", field))
|
||||
})?;
|
||||
Ok(Some(global.get()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spec(name: &str) {
|
||||
println!("running test: {}", name);
|
||||
try_spec(name).expect("Failed to run spec");
|
||||
println!("running test: {}", name);
|
||||
try_spec(name).expect("Failed to run spec");
|
||||
}
|
||||
|
||||
fn try_spec(name: &str) -> Result<(), Error> {
|
||||
let mut spec_driver = SpecDriver::new();
|
||||
let spec_script_path = format!("tests/spec/testsuite/{}.wast", name);
|
||||
let mut spec_driver = SpecDriver::new();
|
||||
let spec_script_path = format!("tests/spec/testsuite/{}.wast", name);
|
||||
|
||||
use std::io::Read;
|
||||
let mut spec_source = Vec::new();
|
||||
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");
|
||||
use std::io::Read;
|
||||
let mut spec_source = Vec::new();
|
||||
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");
|
||||
|
||||
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)).expect("Can't read spec script");
|
||||
let mut errors = vec![];
|
||||
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name))
|
||||
.expect("Can't read spec script");
|
||||
let mut errors = vec![];
|
||||
|
||||
while let Some(Command { kind, line }) = parser.next()? {
|
||||
macro_rules! assert_eq {
|
||||
($a:expr, $b:expr) => {{
|
||||
let (a, b) = ($a, $b);
|
||||
while let Some(Command { kind, line }) = parser.next()? {
|
||||
macro_rules! assert_eq {
|
||||
($a:expr, $b:expr) => {{
|
||||
let (a, b) = ($a, $b);
|
||||
|
||||
if a != b {
|
||||
errors.push(format!(
|
||||
r#"ERROR (line {}):
|
||||
if a != b {
|
||||
errors.push(format!(
|
||||
r#"ERROR (line {}):
|
||||
expected: {:?}
|
||||
got: {:?}
|
||||
"#,
|
||||
line, b, a,
|
||||
));
|
||||
}
|
||||
}};
|
||||
}
|
||||
line, b, a,
|
||||
));
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
println!("Running spec cmd {}: {:?}", line, kind);
|
||||
println!("Running spec cmd {}: {:?}", line, kind);
|
||||
|
||||
match kind {
|
||||
CommandKind::Module { name, module, .. } => {
|
||||
load_module(&module.into_vec(), &name, &mut spec_driver)
|
||||
.expect("Failed to load module");
|
||||
}
|
||||
CommandKind::AssertReturn { action, expected } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
let spec_expected = expected
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
|
||||
for (actual_result, spec_expected) in
|
||||
actual_result.iter().zip(spec_expected.iter())
|
||||
{
|
||||
assert_eq!(actual_result.value_type(), spec_expected.value_type());
|
||||
// f32::NAN != f32::NAN
|
||||
match spec_expected {
|
||||
&RuntimeValue::F32(val) if val.is_nan() => match actual_result {
|
||||
&RuntimeValue::F32(val) => assert!(val.is_nan()),
|
||||
_ => unreachable!(), // checked above that types are same
|
||||
},
|
||||
&RuntimeValue::F64(val) if val.is_nan() => match actual_result {
|
||||
&RuntimeValue::F64(val) => assert!(val.is_nan()),
|
||||
_ => unreachable!(), // checked above that types are same
|
||||
},
|
||||
spec_expected @ _ => assert_eq!(actual_result, spec_expected),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Expected action to return value, got error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertReturnCanonicalNan { action }
|
||||
| CommandKind::AssertReturnArithmeticNan { action } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
|
||||
match actual_result {
|
||||
RuntimeValue::F32(val) => if !val.is_nan() {
|
||||
panic!("Expected nan value, got {:?}", val)
|
||||
},
|
||||
RuntimeValue::F64(val) => if !val.is_nan() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertExhaustion { action, .. } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
|
||||
Err(_e) => {},
|
||||
}
|
||||
}
|
||||
CommandKind::AssertTrap { action, .. } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
panic!(
|
||||
"Expected action to result in a trap, got result: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertInvalid { module, .. }
|
||||
| CommandKind::AssertMalformed { module, .. }
|
||||
| CommandKind::AssertUnlinkable { module, .. } => {
|
||||
let module_load = try_load(&module.into_vec(), &mut spec_driver);
|
||||
match module_load {
|
||||
Ok(_) => panic!("Expected invalid module definition, got some module!"),
|
||||
Err(_e) => {},
|
||||
}
|
||||
}
|
||||
CommandKind::AssertUninstantiable { module, .. } => {
|
||||
match try_load(&module.into_vec(), &mut spec_driver) {
|
||||
Ok(_) => panic!("Expected error running start function at line {}", line),
|
||||
Err(_e) => {},
|
||||
}
|
||||
}
|
||||
CommandKind::Register { name, as_name, .. } => {
|
||||
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
|
||||
Ok(module) => module,
|
||||
Err(e) => panic!("No such module, at line {} - ({:?})", e, line),
|
||||
};
|
||||
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),
|
||||
},
|
||||
}
|
||||
}
|
||||
match kind {
|
||||
CommandKind::Module { name, module, .. } => {
|
||||
load_module(&module.into_vec(), &name, &mut spec_driver)
|
||||
.expect("Failed to load module");
|
||||
}
|
||||
CommandKind::AssertReturn { action, expected } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
let spec_expected = expected
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
|
||||
for (actual_result, spec_expected) in
|
||||
actual_result.iter().zip(spec_expected.iter())
|
||||
{
|
||||
assert_eq!(actual_result.value_type(), spec_expected.value_type());
|
||||
// f32::NAN != f32::NAN
|
||||
match spec_expected {
|
||||
&RuntimeValue::F32(val) if val.is_nan() => match actual_result {
|
||||
&RuntimeValue::F32(val) => assert!(val.is_nan()),
|
||||
_ => unreachable!(), // checked above that types are same
|
||||
},
|
||||
&RuntimeValue::F64(val) if val.is_nan() => match actual_result {
|
||||
&RuntimeValue::F64(val) => assert!(val.is_nan()),
|
||||
_ => unreachable!(), // checked above that types are same
|
||||
},
|
||||
spec_expected @ _ => assert_eq!(actual_result, spec_expected),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Expected action to return value, got error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertReturnCanonicalNan { action }
|
||||
| CommandKind::AssertReturnArithmeticNan { action } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
|
||||
match actual_result {
|
||||
RuntimeValue::F32(val) => {
|
||||
if !val.is_nan() {
|
||||
panic!("Expected nan value, got {:?}", val)
|
||||
}
|
||||
}
|
||||
RuntimeValue::F64(val) => {
|
||||
if !val.is_nan() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertExhaustion { action, .. } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertTrap { action, .. } => {
|
||||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => {
|
||||
panic!(
|
||||
"Expected action to result in a trap, got result: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertInvalid { module, .. }
|
||||
| CommandKind::AssertMalformed { module, .. }
|
||||
| CommandKind::AssertUnlinkable { module, .. } => {
|
||||
let module_load = try_load(&module.into_vec(), &mut spec_driver);
|
||||
match module_load {
|
||||
Ok(_) => panic!("Expected invalid module definition, got some module!"),
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertUninstantiable { module, .. } => {
|
||||
match try_load(&module.into_vec(), &mut spec_driver) {
|
||||
Ok(_) => panic!("Expected error running start function at line {}", line),
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::Register { name, as_name, .. } => {
|
||||
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
|
||||
Ok(module) => module,
|
||||
Err(e) => panic!("No such module, at line {} - ({:?})", e, line),
|
||||
};
|
||||
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() {
|
||||
use std::fmt::Write;
|
||||
let mut out = "\n".to_owned();
|
||||
for err in errors {
|
||||
write!(out, "{}", err).expect("Error formatting errors");
|
||||
}
|
||||
panic!(out);
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
use std::fmt::Write;
|
||||
let mut out = "\n".to_owned();
|
||||
for err in errors {
|
||||
write!(out, "{}", err).expect("Error formatting errors");
|
||||
}
|
||||
panic!(out);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Official spec testsuite.
|
||||
|
||||
extern crate wasmi;
|
||||
extern crate wabt;
|
||||
extern crate wasmi;
|
||||
|
||||
mod spec;
|
||||
|
|
Loading…
Reference in New Issue