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

View File

@ -1,7 +1,6 @@
use std::env;
use std::process;
fn main() {
println!("cargo:rerun-if-changed=./wasm-kernel/");
@ -23,9 +22,9 @@ fn main() {
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),
status = output.status,
stdout = String::from_utf8_lossy(&output.stdout),
stderr = String::from_utf8_lossy(&output.stderr),
);
panic!("{}", msg);
}

View File

@ -4,7 +4,7 @@ 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::*;
@ -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)
);
}

View File

@ -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("")
);
}

View File

@ -1,14 +1,13 @@
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)]
@ -64,9 +63,7 @@ mod tictactoe {
impl Game {
pub fn new() -> Game {
Game {
board: [None; 9],
}
Game { board: [None; 9] }
}
pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
@ -96,12 +93,10 @@ mod tictactoe {
(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),
@ -161,7 +156,7 @@ impl<'a> Externals for Runtime<'a> {
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
Ok(Some(val.into()))
}
_ => panic!("unknown function index")
_ => panic!("unknown function index"),
}
}
}
@ -175,15 +170,20 @@ impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
_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)
)
)
"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)
}
@ -201,8 +201,7 @@ fn instantiate(path: &str) -> Result<ModuleRef, Error> {
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)
}

View File

@ -5,12 +5,12 @@ 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::*;
@ -46,7 +46,8 @@ impl ModuleImportResolver for ResolveAll {
Ok(MemoryInstance::alloc(
Pages(memory_type.initial() as usize),
memory_type.maximum().map(|m| Pages(m as usize)),
).unwrap())
)
.unwrap())
}
fn resolve_table(
@ -75,7 +76,8 @@ fn main() {
.with_resolver("global.Math", &ResolveAll)
.with_resolver("asm2wasm", &ResolveAll)
.with_resolver("spectest", &ResolveAll),
).expect("Failed to instantiate module")
)
.expect("Failed to instantiate module")
.run_start(&mut NopExternals)
.expect("Failed to run start function in module");
}

View File

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

View File

@ -1,9 +1,9 @@
#[allow(unused_imports)]
use alloc::prelude::*;
use core::fmt;
#[cfg(feature = "std")]
use std::error;
use core::fmt;
#[derive(Debug)]
pub struct Error(String);
@ -23,18 +23,24 @@ impl error::Error for Error {
/// Stack with limit.
#[derive(Debug)]
pub struct StackWithLimit<T> where T: Clone {
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 {
impl<T> StackWithLimit<T>
where
T: Clone,
{
pub fn with_limit(limit: usize) -> Self {
StackWithLimit {
values: Vec::new(),
limit: limit
limit: limit,
}
}
@ -60,10 +66,17 @@ impl<T> StackWithLimit<T> where T: Clone {
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())));
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> {

View File

@ -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).
///
@ -58,17 +58,10 @@ pub(crate) enum FuncInstanceInternal {
impl fmt::Debug for FuncInstance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.as_internal() {
&FuncInstanceInternal::Internal {
ref signature,
..
} => {
&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,
)
write!(f, "Internal {{ signature={:?} }}", signature,)
}
&FuncInstanceInternal::Host { ref signature, .. } => {
write!(f, "Host {{ signature={:?} }}", signature)
@ -186,15 +179,13 @@ impl FuncInstance {
FuncInstanceInternal::Host {
ref host_func_index,
..
} => {
Ok(FuncInvocation {
} => Ok(FuncInvocation {
kind: FuncInvocationKind::Host {
args,
host_func_index: *host_func_index,
finished: false,
},
})
},
}),
}
}
}
@ -239,7 +230,7 @@ enum FuncInvocationKind<'args> {
Host {
args: &'args [RuntimeValue],
host_func_index: usize,
finished: bool
finished: bool,
},
}
@ -255,32 +246,37 @@ impl<'args> FuncInvocation<'args> {
/// 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() {
&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> {
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 } => {
}
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())?)
},
}
}
}
@ -292,17 +288,21 @@ impl<'args> FuncInvocation<'args> {
///
/// [`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> {
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);
},
}
}
}
}

View File

@ -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).
///
@ -57,7 +57,9 @@ impl GlobalInstance {
/// 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()));
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()));

View File

@ -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.
@ -27,8 +27,14 @@ impl<'a> RuntimeArgs<'a> {
/// # 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)?)
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`.
@ -50,7 +56,10 @@ impl<'a> RuntimeArgs<'a> {
/// # Panics
///
/// Panics if cast is invalid or not enough arguments.
pub fn nth<T>(&self, idx: usize) -> T where T: FromRuntimeValue {
pub fn nth<T>(&self, idx: usize) -> T
where
T: FromRuntimeValue,
{
let value = self.nth_value_checked(idx).expect("Invalid argument index");
value.try_into().expect("Unexpected argument type")
}
@ -231,8 +240,8 @@ impl Externals for NopExternals {
#[cfg(test)]
mod tests {
use super::{HostError, RuntimeArgs};
use value::RuntimeValue;
use super::{RuntimeArgs, HostError};
#[test]
fn i32_runtime_args() {
@ -248,6 +257,5 @@ mod tests {
}
// Tests that `HostError` trait is object safe.
fn _host_error_is_object_safe(_: &HostError) {
}
fn _host_error_is_object_safe(_: &HostError) {}
}

View File

@ -1,20 +1,19 @@
#[allow(unused_imports)]
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,7 +26,6 @@ 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.
@ -120,7 +118,9 @@ impl<'a> Default for ImportsBuilder<'a> {
impl<'a> ImportsBuilder<'a> {
/// Create an empty `ImportsBuilder`.
pub fn new() -> ImportsBuilder<'a> {
ImportsBuilder { modules: HashMap::new() }
ImportsBuilder {
modules: HashMap::new(),
}
}
/// Register an resolver by a name.
@ -152,9 +152,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
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)
self.resolver(module_name)
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
.resolve_func(field_name, signature)
}
fn resolve_global(
@ -163,9 +163,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
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)
self.resolver(module_name)
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
.resolve_global(field_name, global_type)
}
fn resolve_memory(
@ -174,9 +174,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
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)
self.resolver(module_name)
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
.resolve_memory(field_name, memory_type)
}
fn resolve_table(
@ -185,9 +185,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
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)
self.resolver(module_name)
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
.resolve_table(field_name, table_type)
}
}
@ -200,14 +200,11 @@ pub trait ModuleImportResolver {
/// 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),
))
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.
@ -220,9 +217,10 @@ pub trait ModuleImportResolver {
field_name: &str,
_global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> {
Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
/// Resolve a memory.
@ -235,9 +233,10 @@ pub trait ModuleImportResolver {
field_name: &str,
_memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> {
Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
/// Resolve a table.
@ -250,22 +249,18 @@ pub trait ModuleImportResolver {
field_name: &str,
_table_type: &TableDescriptor,
) -> Result<TableRef, Error> {
Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
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))
})?
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(|| {
@ -278,10 +273,9 @@ impl ModuleImportResolver for ModuleRef {
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))
})?
Ok(self
.export_by_name(field_name)
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
.as_global()
.cloned()
.ok_or_else(|| {
@ -294,10 +288,9 @@ impl ModuleImportResolver for ModuleRef {
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))
})?
Ok(self
.export_by_name(field_name)
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
.as_memory()
.cloned()
.ok_or_else(|| {
@ -310,14 +303,11 @@ impl ModuleImportResolver for ModuleRef {
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))
})?
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))
})?)
.ok_or_else(|| Error::Instantiation(format!("Export {} is not a table", field_name)))?)
}
}

View File

@ -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::*;
@ -292,7 +290,7 @@ impl Error {
Error::Trap(ref trap) => match *trap.kind() {
TrapKind::Host(ref host_err) => Some(&**host_err),
_ => None,
}
},
_ => None,
}
}
@ -347,13 +345,19 @@ impl error::Error for Error {
}
}
impl<U> From<U> for Error where U: host::HostError + Sized {
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 {
impl<U> From<U> for Trap
where
U: host::HostError + Sized,
{
fn from(e: U) -> Self {
Trap::new(TrapKind::Host(Box::new(e)))
}
@ -377,38 +381,38 @@ impl From<validation::Error> for Error {
}
}
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::{size_of, ByteSize, Bytes, RoundUpTo};
}
/// Deserialized module prepared for instantiation.
@ -452,15 +456,9 @@ impl Module {
/// ```
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)?;
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

View File

@ -1,15 +1,15 @@
#[allow(unused_imports)]
use alloc::prelude::*;
use alloc::rc::Rc;
use core::u32;
use core::ops::Range;
use core::cell::{Cell, RefCell};
use core::cmp;
use core::fmt;
use core::cell::{Cell, RefCell};
use core::ops::Range;
use core::u32;
use memory_units::{Bytes, Pages, RoundUpTo};
use parity_wasm::elements::ResizableLimits;
use Error;
use memory_units::{RoundUpTo, Pages, Bytes};
use value::LittleEndianConvert;
use Error;
/// Size of a page of [linear memory][`MemoryInstance`] - 64KiB.
///
@ -78,7 +78,7 @@ struct CheckedRegion {
impl CheckedRegion {
fn range(&self) -> Range<usize> {
self.offset..self.offset+self.size
self.offset..self.offset + self.size
}
fn intersects(&self, other: &Self) -> bool {
@ -174,7 +174,8 @@ impl MemoryInstance {
/// Get value from memory at given offset.
pub fn get_value<T: LittleEndianConvert>(&self, offset: u32) -> Result<T, Error> {
let mut buffer = self.buffer.borrow_mut();
let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
let region =
self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
Ok(T::from_little_endian(&buffer[region.range()]).expect("Slice size is checked"))
}
@ -208,7 +209,9 @@ impl MemoryInstance {
/// Copy data in the memory at given offset.
pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut();
let range = self.checked_region(&mut buffer, offset as usize, value.len())?.range();
let range = self
.checked_region(&mut buffer, offset as usize, value.len())?
.range();
buffer[range].copy_from_slice(value);
@ -218,7 +221,9 @@ impl MemoryInstance {
/// Copy value in the memory at given offset.
pub fn set_value<T: LittleEndianConvert>(&self, offset: u32, value: T) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut();
let range = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?.range();
let range = self
.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?
.range();
value.into_little_endian(&mut buffer[range]);
Ok(())
}
@ -255,18 +260,33 @@ impl MemoryInstance {
Ok(size_before_grow)
}
fn checked_region<B>(&self, buffer: &mut B, offset: usize, size: usize) -> Result<CheckedRegion, Error>
where B: ::core::ops::DerefMut<Target=Vec<u8>>
fn checked_region<B>(
&self,
buffer: &mut B,
offset: usize,
size: usize,
) -> Result<CheckedRegion, Error>
where
B: ::core::ops::DerefMut<Target = Vec<u8>>,
{
let end = offset.checked_add(size)
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;
let end = offset.checked_add(size).ok_or_else(|| {
Error::Memory(format!(
"trying to access memory block of size {} from offset {}",
size, offset
))
})?;
if end <= self.current_size.get() && buffer.len() < end {
buffer.resize(end, 0);
}
if end > buffer.len() {
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len())));
return Err(Error::Memory(format!(
"trying to access region [{}..{}] in memory [0..{}]",
offset,
end,
buffer.len()
)));
}
Ok(CheckedRegion {
@ -275,15 +295,30 @@ impl MemoryInstance {
})
}
fn checked_region_pair<B>(&self, buffer: &mut B, offset1: usize, size1: usize, offset2: usize, size2: usize)
-> Result<(CheckedRegion, CheckedRegion), Error>
where B: ::core::ops::DerefMut<Target=Vec<u8>>
fn checked_region_pair<B>(
&self,
buffer: &mut B,
offset1: usize,
size1: usize,
offset2: usize,
size2: usize,
) -> Result<(CheckedRegion, CheckedRegion), Error>
where
B: ::core::ops::DerefMut<Target = Vec<u8>>,
{
let end1 = offset1.checked_add(size1)
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size1, offset1)))?;
let end1 = offset1.checked_add(size1).ok_or_else(|| {
Error::Memory(format!(
"trying to access memory block of size {} from offset {}",
size1, offset1
))
})?;
let end2 = offset2.checked_add(size2)
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size2, offset2)))?;
let end2 = offset2.checked_add(size2).ok_or_else(|| {
Error::Memory(format!(
"trying to access memory block of size {} from offset {}",
size2, offset2
))
})?;
let max = cmp::max(end1, end2);
if max <= self.current_size.get() && buffer.len() < max {
@ -291,16 +326,32 @@ impl MemoryInstance {
}
if end1 > buffer.len() {
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset1, end1, buffer.len())));
return Err(Error::Memory(format!(
"trying to access region [{}..{}] in memory [0..{}]",
offset1,
end1,
buffer.len()
)));
}
if end2 > buffer.len() {
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset2, end2, buffer.len())));
return Err(Error::Memory(format!(
"trying to access region [{}..{}] in memory [0..{}]",
offset2,
end2,
buffer.len()
)));
}
Ok((
CheckedRegion { offset: offset1, size: size1 },
CheckedRegion { offset: offset2, size: size2 },
CheckedRegion {
offset: offset1,
size: size1,
},
CheckedRegion {
offset: offset2,
size: size2,
},
))
}
@ -314,13 +365,16 @@ impl MemoryInstance {
pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut();
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
let (read_region, write_region) =
self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
unsafe { ::core::ptr::copy(
unsafe {
::core::ptr::copy(
buffer[read_region.range()].as_ptr(),
buffer[write_region.range()].as_mut_ptr(),
len,
)}
)
}
Ok(())
}
@ -336,20 +390,30 @@ impl MemoryInstance {
///
/// - either of specified regions is out of bounds,
/// - these regions overlaps.
pub fn copy_nonoverlapping(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
pub fn copy_nonoverlapping(
&self,
src_offset: usize,
dst_offset: usize,
len: usize,
) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut();
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
let (read_region, write_region) =
self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
if read_region.intersects(&write_region) {
return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions")))
return Err(Error::Memory(format!(
"non-overlapping copy is used for overlapping regions"
)));
}
unsafe { ::core::ptr::copy_nonoverlapping(
unsafe {
::core::ptr::copy_nonoverlapping(
buffer[read_region.range()].as_ptr(),
buffer[write_region.range()].as_mut_ptr(),
len,
)}
)
}
Ok(())
}
@ -357,7 +421,13 @@ impl MemoryInstance {
/// Copy memory between two (possibly distinct) memory instances.
///
/// If the same memory instance passed as `src` and `dst` then usual `copy` will be used.
pub fn transfer(src: &MemoryRef, src_offset: usize, dst: &MemoryRef, dst_offset: usize, len: usize) -> Result<(), Error> {
pub fn transfer(
src: &MemoryRef,
src_offset: usize,
dst: &MemoryRef,
dst_offset: usize,
len: usize,
) -> Result<(), Error> {
if Rc::ptr_eq(&src.0, &dst.0) {
// `transfer` is invoked with with same source and destination. Let's assume that regions may
// overlap and use `copy`.
@ -369,8 +439,12 @@ impl MemoryInstance {
let mut src_buffer = src.buffer.borrow_mut();
let mut dst_buffer = dst.buffer.borrow_mut();
let src_range = src.checked_region(&mut src_buffer, src_offset, len)?.range();
let dst_range = dst.checked_region(&mut dst_buffer, dst_offset, len)?.range();
let src_range = src
.checked_region(&mut src_buffer, src_offset, len)?
.range();
let dst_range = dst
.checked_region(&mut dst_buffer, dst_offset, len)?
.range();
dst_buffer[dst_range].copy_from_slice(&src_buffer[src_range]);
@ -388,7 +462,9 @@ impl MemoryInstance {
let mut buffer = self.buffer.borrow_mut();
let range = self.checked_region(&mut buffer, offset, len)?.range();
for val in &mut buffer[range] { *val = new_val }
for val in &mut buffer[range] {
*val = new_val
}
Ok(())
}
@ -434,19 +510,24 @@ impl MemoryInstance {
pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), String> {
if initial > LINEAR_MEMORY_MAX_PAGES {
return Err(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
return Err(format!(
"initial memory size must be at most {} pages",
LINEAR_MEMORY_MAX_PAGES.0
));
}
if let Some(maximum) = maximum {
if initial > maximum {
return Err(format!(
"maximum limit {} is less than minimum {}",
maximum.0,
initial.0,
maximum.0, initial.0,
));
}
if maximum > LINEAR_MEMORY_MAX_PAGES {
return Err(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
return Err(format!(
"maximum memory size must be at most {} pages",
LINEAR_MEMORY_MAX_PAGES.0
));
}
}
Ok(())
@ -455,10 +536,10 @@ pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), Str
#[cfg(test)]
mod tests {
use super::{MemoryRef, MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
use super::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
use memory_units::Pages;
use std::rc::Rc;
use Error;
use memory_units::Pages;
#[test]
fn alloc() {
@ -493,11 +574,7 @@ mod tests {
if result.is_ok() != expected_ok {
panic!(
"unexpected error at {}, initial={:?}, max={:?}, expected={}, result={:?}",
index,
initial,
maybe_max,
expected_ok,
result,
index, initial, maybe_max, expected_ok, result,
);
}
}
@ -511,7 +588,8 @@ mod tests {
fn create_memory(initial_content: &[u8]) -> MemoryInstance {
let mem = MemoryInstance::new(Pages(1), Some(Pages(1)));
mem.set(0, initial_content).expect("Successful initialize the memory");
mem.set(0, initial_content)
.expect("Successful initialize the memory");
mem
}
@ -534,7 +612,8 @@ mod tests {
#[test]
fn copy_nonoverlapping() {
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
mem.copy_nonoverlapping(0, 10, 10).expect("Successfully copy the elements");
mem.copy_nonoverlapping(0, 10, 10)
.expect("Successfully copy the elements");
let result = mem.get(10, 10).expect("Successfully retrieve the result");
assert_eq!(result, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
@ -544,7 +623,7 @@ mod tests {
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
let result = mem.copy_nonoverlapping(0, 4, 6);
match result {
Err(Error::Memory(_)) => {},
Err(Error::Memory(_)) => {}
_ => panic!("Expected Error::Memory(_) result, but got {:?}", result),
}
}
@ -554,7 +633,7 @@ mod tests {
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
let result = mem.copy_nonoverlapping(4, 0, 6);
match result {
Err(Error::Memory(_)) => {},
Err(Error::Memory(_)) => {}
_ => panic!("Expected Error::Memory(_), but got {:?}", result),
}
}
@ -562,12 +641,17 @@ mod tests {
#[test]
fn transfer_works() {
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
let dst = MemoryRef(Rc::new(create_memory(&[
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
])));
MemoryInstance::transfer(&src, 4, &dst, 0, 3).unwrap();
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(dst.get(0, 10).unwrap(), &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]);
assert_eq!(
dst.get(0, 10).unwrap(),
&[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]
);
}
#[test]
@ -591,19 +675,25 @@ mod tests {
#[test]
fn transfer_oob_errors() {
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
let dst = MemoryRef(Rc::new(create_memory(&[
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
])));
assert!(MemoryInstance::transfer(&src, 65535, &dst, 0, 3).is_err());
// Check that memories content left untouched
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(dst.get(0, 10).unwrap(), &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
assert_eq!(
dst.get(0, 10).unwrap(),
&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
);
}
#[test]
fn clear() {
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
mem.clear(0, 0x4A, 10).expect("To successfully clear the memory");
mem.clear(0, 0x4A, 10)
.expect("To successfully clear the memory");
let result = mem.get(0, 10).expect("To successfully retrieve the result");
assert_eq!(result, &[0x4A; 10]);
}
@ -611,10 +701,12 @@ mod tests {
#[test]
fn get_into() {
let mem = MemoryInstance::new(Pages(1), None);
mem.set(6, &[13, 17, 129]).expect("memory set should not fail");
mem.set(6, &[13, 17, 129])
.expect("memory set should not fail");
let mut data = [0u8; 2];
mem.get_into(7, &mut data[..]).expect("get_into should not fail");
mem.get_into(7, &mut data[..])
.expect("get_into should not fail");
assert_eq!(data, [17, 129]);
}

View File

@ -1,26 +1,26 @@
#[allow(unused_imports)]
use alloc::prelude::*;
use alloc::rc::Rc;
use Trap;
use core::cell::RefCell;
use core::fmt;
use Trap;
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(not(feature = "std"))]
use hashmap_core::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type};
use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance};
use imports::ImportResolver;
use global::{GlobalInstance, GlobalRef};
use func::{FuncRef, FuncBody, FuncInstance};
use table::TableRef;
use memory::MemoryRef;
use host::Externals;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
use func::{FuncBody, FuncInstance, FuncRef};
use global::{GlobalInstance, GlobalRef};
use host::Externals;
use imports::ImportResolver;
use memory::MemoryRef;
use memory_units::Pages;
use parity_wasm::elements::{External, InitExpr, Instruction, Internal, ResizableLimits, Type};
use table::TableRef;
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
use {Error, MemoryInstance, Module, RuntimeValue, Signature, TableInstance};
/// Reference to a [`ModuleInstance`].
///
@ -254,9 +254,9 @@ impl ModuleInstance {
match (import.external(), extern_val) {
(&External::Function(fn_type_idx), &ExternVal::Func(ref func)) => {
let expected_fn_type = instance.signature_by_index(fn_type_idx).expect(
"Due to validation function type should exists",
);
let expected_fn_type = instance
.signature_by_index(fn_type_idx)
.expect("Due to validation function type should exists");
let actual_fn_type = func.signature();
if &*expected_fn_type != actual_fn_type {
return Err(Error::Instantiation(format!(
@ -289,8 +289,7 @@ impl ModuleInstance {
(expected_import, actual_extern_val) => {
return Err(Error::Instantiation(format!(
"Expected {:?} type, but provided {:?} extern_val",
expected_import,
actual_extern_val
expected_import, actual_extern_val
)));
}
}
@ -299,9 +298,10 @@ impl ModuleInstance {
let code = loaded_module.code();
{
let funcs = module.function_section().map(|fs| fs.entries()).unwrap_or(
&[],
);
let funcs = module
.function_section()
.map(|fs| fs.entries())
.unwrap_or(&[]);
let bodies = module.code_section().map(|cs| cs.bodies()).unwrap_or(&[]);
debug_assert!(
funcs.len() == bodies.len(),
@ -311,9 +311,9 @@ impl ModuleInstance {
for (index, (ty, body)) in
Iterator::zip(funcs.into_iter(), bodies.into_iter()).enumerate()
{
let signature = instance.signature_by_index(ty.type_ref()).expect(
"Due to validation type should exists",
);
let signature = instance
.signature_by_index(ty.type_ref())
.expect("Due to validation type should exists");
let code = code.get(index).expect(
"At func validation time labels are collected; Collected labels are added by index; qed",
).clone();
@ -328,16 +328,15 @@ impl ModuleInstance {
}
for table_type in module.table_section().map(|ts| ts.entries()).unwrap_or(&[]) {
let table = TableInstance::alloc(
table_type.limits().initial(),
table_type.limits().maximum(),
)?;
let table =
TableInstance::alloc(table_type.limits().initial(), table_type.limits().maximum())?;
instance.push_table(table);
}
for memory_type in module.memory_section().map(|ms| ms.entries()).unwrap_or(
&[],
)
for memory_type in module
.memory_section()
.map(|ms| ms.entries())
.unwrap_or(&[])
{
let initial: Pages = Pages(memory_type.limits().initial() as usize);
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
@ -347,46 +346,45 @@ impl ModuleInstance {
instance.push_memory(memory);
}
for global_entry in module.global_section().map(|gs| gs.entries()).unwrap_or(
&[],
)
for global_entry in module
.global_section()
.map(|gs| gs.entries())
.unwrap_or(&[])
{
let init_val = eval_init_expr(global_entry.init_expr(), &*instance);
let global = GlobalInstance::alloc(
init_val,
global_entry.global_type().is_mutable(),
);
let global = GlobalInstance::alloc(init_val, global_entry.global_type().is_mutable());
instance.push_global(global);
}
for export in module.export_section().map(|es| es.entries()).unwrap_or(
&[],
)
for export in module
.export_section()
.map(|es| es.entries())
.unwrap_or(&[])
{
let field = export.field();
let extern_val: ExternVal = match *export.internal() {
Internal::Function(idx) => {
let func = instance.func_by_index(idx).expect(
"Due to validation func should exists",
);
let func = instance
.func_by_index(idx)
.expect("Due to validation func should exists");
ExternVal::Func(func)
}
Internal::Global(idx) => {
let global = instance.global_by_index(idx).expect(
"Due to validation global should exists",
);
let global = instance
.global_by_index(idx)
.expect("Due to validation global should exists");
ExternVal::Global(global)
}
Internal::Memory(idx) => {
let memory = instance.memory_by_index(idx).expect(
"Due to validation memory should exists",
);
let memory = instance
.memory_by_index(idx)
.expect("Due to validation memory should exists");
ExternVal::Memory(memory)
}
Internal::Table(idx) => {
let table = instance.table_by_index(idx).expect(
"Due to validation table should exists",
);
let table = instance
.table_by_index(idx)
.expect("Due to validation table should exists");
ExternVal::Table(table)
}
};
@ -410,31 +408,34 @@ impl ModuleInstance {
let module_ref = ModuleInstance::alloc_module(loaded_module, extern_vals)?;
for element_segment in module.elements_section().map(|es| es.entries()).unwrap_or(
&[],
)
for element_segment in module
.elements_section()
.map(|es| es.entries())
.unwrap_or(&[])
{
let offset_val = match eval_init_expr(element_segment.offset(), &module_ref) {
RuntimeValue::I32(v) => v as u32,
_ => panic!("Due to validation elem segment offset should evaluate to i32"),
};
let table_inst = module_ref.table_by_index(DEFAULT_TABLE_INDEX).expect(
"Due to validation default table should exists",
);
let table_inst = module_ref
.table_by_index(DEFAULT_TABLE_INDEX)
.expect("Due to validation default table should exists");
// This check is not only for bailing out early, but also to check the case when
// segment consist of 0 members.
if offset_val as u64 + element_segment.members().len() as u64 > table_inst.current_size() as u64 {
return Err(
Error::Instantiation("elements segment does not fit".to_string())
);
if offset_val as u64 + element_segment.members().len() as u64
> table_inst.current_size() as u64
{
return Err(Error::Instantiation(
"elements segment does not fit".to_string(),
));
}
for (j, func_idx) in element_segment.members().into_iter().enumerate() {
let func = module_ref.func_by_index(*func_idx).expect(
"Due to validation funcs from element segments should exists",
);
let func = module_ref
.func_by_index(*func_idx)
.expect("Due to validation funcs from element segments should exists");
table_inst.set(offset_val + j as u32, Some(func))?;
}
@ -446,9 +447,9 @@ impl ModuleInstance {
_ => panic!("Due to validation data segment offset should evaluate to i32"),
};
let memory_inst = module_ref.memory_by_index(DEFAULT_MEMORY_INDEX).expect(
"Due to validation default memory should exists",
);
let memory_inst = module_ref
.memory_by_index(DEFAULT_MEMORY_INDEX)
.expect("Due to validation default memory should exists");
memory_inst.set(offset_val, data_segment.value())?;
}
@ -540,17 +541,20 @@ impl ModuleInstance {
}
External::Table(ref table_type) => {
let table_descriptor = TableDescriptor::from_elements(table_type);
let table = imports.resolve_table(module_name, field_name, &table_descriptor)?;
let table =
imports.resolve_table(module_name, field_name, &table_descriptor)?;
ExternVal::Table(table)
}
External::Memory(ref memory_type) => {
let memory_descriptor = MemoryDescriptor::from_elements(memory_type);
let memory = imports.resolve_memory(module_name, field_name, &memory_descriptor)?;
let memory =
imports.resolve_memory(module_name, field_name, &memory_descriptor)?;
ExternVal::Memory(memory)
}
External::Global(ref global_type) => {
let global_descriptor = GlobalDescriptor::from_elements(global_type);
let global = imports.resolve_global(module_name, field_name, &global_descriptor)?;
let global =
imports.resolve_global(module_name, field_name, &global_descriptor)?;
ExternVal::Global(global)
}
};
@ -614,23 +618,21 @@ impl ModuleInstance {
args: &[RuntimeValue],
externals: &mut E,
) -> Result<Option<RuntimeValue>, Error> {
let extern_val = self.export_by_name(func_name).ok_or_else(|| {
Error::Function(format!("Module doesn't have export {}", func_name))
})?;
let extern_val = self
.export_by_name(func_name)
.ok_or_else(|| Error::Function(format!("Module doesn't have export {}", func_name)))?;
let func_instance = match extern_val {
ExternVal::Func(func_instance) => func_instance,
unexpected => {
return Err(Error::Function(format!(
"Export {} is not a function, but {:?}",
func_name,
unexpected
func_name, unexpected
)));
}
};
FuncInstance::invoke(&func_instance, args, externals)
.map_err(|t| Error::Trap(t))
FuncInstance::invoke(&func_instance, args, externals).map_err(|t| Error::Trap(t))
}
/// Find export by a name.
@ -685,9 +687,10 @@ impl<'a> NotStartedModuleRef<'a> {
/// Returns `Err` if start function traps.
pub fn run_start<E: Externals>(self, state: &mut E) -> Result<ModuleRef, Trap> {
if let Some(start_fn_idx) = self.loaded_module.module().start_section() {
let start_func = self.instance.func_by_index(start_fn_idx).expect(
"Due to validation start function should exists",
);
let start_func = self
.instance
.func_by_index(start_fn_idx)
.expect("Due to validation start function should exists");
FuncInstance::invoke(&start_func, &[], state)?;
}
Ok(self.instance)
@ -718,9 +721,9 @@ fn eval_init_expr(init_expr: &InitExpr, module: &ModuleInstance) -> RuntimeValue
Instruction::F32Const(v) => RuntimeValue::decode_f32(v),
Instruction::F64Const(v) => RuntimeValue::decode_f64(v),
Instruction::GetGlobal(idx) => {
let global = module.global_by_index(idx).expect(
"Due to validation global should exists in module",
);
let global = module
.global_by_index(idx)
.expect("Due to validation global should exists in module");
global.get()
}
_ => panic!("Due to validation init should be a const expr"),
@ -744,7 +747,7 @@ fn match_limits(l1: &ResizableLimits, l2: &ResizableLimits) -> Result<(), Error>
"trying to import with limits l1.max={:?} and l2.max={:?}",
l1.maximum(),
l2.maximum()
)))
)));
}
}
@ -765,14 +768,13 @@ pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> {
Ok(())
}
#[cfg(test)]
mod tests {
use imports::ImportsBuilder;
use super::{ExternVal, ModuleInstance};
use func::FuncInstance;
use types::{Signature, ValueType};
use super::{ModuleInstance, ExternVal};
use imports::ImportsBuilder;
use tests::parse_wat;
use types::{Signature, ValueType};
#[should_panic]
#[test]
@ -782,12 +784,11 @@ mod tests {
(module
(func $f)
(start $f))
"#
"#,
);
ModuleInstance::new(
&module_with_start,
&ImportsBuilder::default()
).unwrap().assert_no_start();
ModuleInstance::new(&module_with_start, &ImportsBuilder::default())
.unwrap()
.assert_no_start();
}
#[test]
@ -800,40 +801,39 @@ mod tests {
"#,
);
assert!(
ModuleInstance::with_externvals(
assert!(ModuleInstance::with_externvals(
&module_with_single_import,
[
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0),)
].iter(),
).is_ok()
);
[ExternVal::Func(FuncInstance::alloc_host(
Signature::new(&[][..], None),
0
),)]
.iter(),
)
.is_ok());
// externval vector is longer than import count.
assert!(
ModuleInstance::with_externvals(
assert!(ModuleInstance::with_externvals(
&module_with_single_import,
[
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0)),
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 1)),
].iter(),
).is_err()
);
]
.iter(),
)
.is_err());
// externval vector is shorter than import count.
assert!(ModuleInstance::with_externvals(&module_with_single_import, [].iter(),).is_err());
// externval vector has an unexpected type.
assert!(
ModuleInstance::with_externvals(
assert!(ModuleInstance::with_externvals(
&module_with_single_import,
[
ExternVal::Func(FuncInstance::alloc_host(
[ExternVal::Func(FuncInstance::alloc_host(
Signature::new(&[][..], Some(ValueType::I32)),
0
),)
].iter(),
).is_err()
);
),)]
.iter(),
)
.is_err());
}
}

View File

@ -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

View File

@ -1347,7 +1347,8 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu
.any(|(expected_type, param_value)| {
let actual_type = param_value.value_type();
&actual_type != expected_type
}) {
})
{
return Err(TrapKind::UnexpectedSignature.into());
}

View File

@ -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).
///
@ -106,7 +106,9 @@ impl TableInstance {
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)
let new_size = self
.current_size()
.checked_add(by)
.and_then(|new_size| {
if maximum_size < new_size {
None
@ -114,13 +116,13 @@ impl TableInstance {
Some(new_size)
}
})
.ok_or_else(||
.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(())
}
@ -129,13 +131,12 @@ impl TableInstance {
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(||
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
)),
)?;
offset, buffer_len
))
})?;
Ok(table_elem)
}
@ -143,13 +144,12 @@ impl TableInstance {
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(||
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
offset, buffer_len
))
)?;
})?;
*table_elem = value;
Ok(())
}

View File

@ -1,11 +1,11 @@
use {
Error, Signature, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
MemoryInstance, MemoryRef, TableInstance, TableRef, ModuleImportResolver, ModuleInstance, ModuleRef,
RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, ResumableError,
};
use types::ValueType;
use memory_units::Pages;
use super::parse_wat;
use memory_units::Pages;
use types::ValueType;
use {
Error, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, MemoryDescriptor,
MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, ResumableError,
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap, TrapKind,
};
#[derive(Debug, Clone, PartialEq)]
struct HostErrorWithCode {
@ -107,9 +107,10 @@ impl Externals for TestHost {
INC_MEM_FUNC_INDEX => {
let ptr: u32 = args.nth(0);
let memory = self.memory.as_ref().expect(
"Function 'inc_mem' expects attached memory",
);
let memory = self
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory");
let mut buf = [0u8; 1];
memory.get_into(ptr, &mut buf).unwrap();
buf[0] += 1;
@ -120,18 +121,22 @@ impl Externals for TestHost {
GET_MEM_FUNC_INDEX => {
let ptr: u32 = args.nth(0);
let memory = self.memory.as_ref().expect(
"Function 'get_mem' expects attached memory",
);
let memory = self
.memory
.as_ref()
.expect("Function 'get_mem' expects attached memory");
let mut buf = [0u8; 1];
memory.get_into(ptr, &mut buf).unwrap();
Ok(Some(RuntimeValue::I32(buf[0] as i32)))
}
RECURSE_FUNC_INDEX => {
let val = args.nth_value_checked(0).expect("Exactly one argument expected");
let val = args
.nth_value_checked(0)
.expect("Exactly one argument expected");
let instance = self.instance
let instance = self
.instance
.as_ref()
.expect("Function 'recurse' expects attached module instance")
.clone();
@ -141,7 +146,9 @@ impl Externals for TestHost {
.expect("expected to be Some");
if val.value_type() != result.value_type() {
return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into());
return Err(
TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into(),
);
}
Ok(Some(result))
}
@ -192,17 +199,17 @@ impl ModuleImportResolver for TestHost {
"recurse" => RECURSE_FUNC_INDEX,
"trap_sub" => TRAP_SUB_FUNC_INDEX,
_ => {
return Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)));
}
};
if !self.check_signature(index, signature) {
return Err(Error::Instantiation(format!(
"Export `{}` doesnt match expected type {:?}",
field_name,
signature
field_name, signature
)));
}
@ -214,9 +221,10 @@ impl ModuleImportResolver for TestHost {
field_name: &str,
_memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> {
Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
}
@ -244,9 +252,9 @@ fn call_host_func() {
.assert_no_start();
assert_eq!(
instance.invoke_export("test", &[], &mut env).expect(
"Failed to invoke 'test' function",
),
instance
.invoke_export("test", &[], &mut env)
.expect("Failed to invoke 'test' function",),
Some(RuntimeValue::I32(-2))
);
}
@ -280,16 +288,16 @@ fn resume_call_host_func() {
let mut invocation = FuncInstance::invoke_resumable(&func_instance, &[]).unwrap();
let result = invocation.start_execution(&mut env);
match result {
Err(ResumableError::Trap(_)) => {},
Err(ResumableError::Trap(_)) => {}
_ => panic!(),
}
assert!(invocation.is_resumable());
let trap_sub_result = env.trap_sub_result.take();
assert_eq!(
invocation.resume_execution(trap_sub_result, &mut env).expect(
"Failed to invoke 'test' function",
),
invocation
.resume_execution(trap_sub_result, &mut env)
.expect("Failed to invoke 'test' function",),
Some(RuntimeValue::I32(-2))
);
}
@ -316,13 +324,15 @@ fn host_err() {
.expect("Failed to instantiate module")
.assert_no_start();
let error = instance.invoke_export("test", &[], &mut env).expect_err(
"`test` expected to return error",
);
let error = instance
.invoke_export("test", &[], &mut env)
.expect_err("`test` expected to return error");
let error_with_code = error.as_host_error().expect("Expected host error").downcast_ref::<HostErrorWithCode>().expect(
"Failed to downcast to expected error type",
);
let error_with_code = error
.as_host_error()
.expect("Expected host error")
.downcast_ref::<HostErrorWithCode>()
.expect("Failed to downcast to expected error type");
assert_eq!(error_with_code.error_code, 228);
}
@ -351,9 +361,9 @@ fn modify_mem_with_host_funcs() {
.expect("Failed to instantiate module")
.assert_no_start();
instance.invoke_export("modify_mem", &[], &mut env).expect(
"Failed to invoke 'test' function",
);
instance
.invoke_export("modify_mem", &[], &mut env)
.expect("Failed to invoke 'test' function");
// Check contents of memory at address 12.
let mut buf = [0u8; 1];
@ -451,9 +461,9 @@ fn recursion() {
env.instance = Some(instance.clone());
assert_eq!(
instance.invoke_export("test", &[], &mut env).expect(
"Failed to invoke 'test' function",
),
instance
.invoke_export("test", &[], &mut env)
.expect("Failed to invoke 'test' function",),
// 363 = 321 + 42
Some(RuntimeValue::I64(363))
);
@ -472,21 +482,17 @@ fn defer_providing_externals() {
}
impl ModuleImportResolver for HostImportResolver {
fn resolve_func(
&self,
field_name: &str,
signature: &Signature,
) -> Result<FuncRef, Error> {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
if field_name != "inc" {
return Err(Error::Instantiation(
format!("Export {} not found", field_name),
));
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)));
}
if signature.params() != &[ValueType::I32] || signature.return_type() != None {
return Err(Error::Instantiation(format!(
"Export `{}` doesnt match expected type {:?}",
field_name,
signature
field_name, signature
)));
}
@ -501,9 +507,10 @@ fn defer_providing_externals() {
if field_name == "mem" {
Ok(self.mem.clone())
} else {
Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
}
}
@ -547,14 +554,16 @@ fn defer_providing_externals() {
// Create HostImportResolver with some initialized memory instance.
// This memory instance will be provided as 'mem' export.
let host_import_resolver =
HostImportResolver { mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap() };
let host_import_resolver = HostImportResolver {
mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap(),
};
// Instantiate module with `host_import_resolver` as import resolver for "host" module.
let instance = ModuleInstance::new(
&module,
&ImportsBuilder::new().with_resolver("host", &host_import_resolver),
).expect("Failed to instantiate module")
)
.expect("Failed to instantiate module")
.assert_no_start();
let mut acc = 89;
@ -599,18 +608,15 @@ fn two_envs_one_externals() {
struct OrdinaryResolver;
impl ModuleImportResolver for PrivilegedResolver {
fn resolve_func(
&self,
field_name: &str,
signature: &Signature,
) -> Result<FuncRef, Error> {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
let index = match field_name {
"ordinary" => ORDINARY_FUNC_INDEX,
"privileged" => PRIVILEGED_FUNC_INDEX,
_ => {
return Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)));
}
};
@ -619,22 +625,19 @@ fn two_envs_one_externals() {
}
impl ModuleImportResolver for OrdinaryResolver {
fn resolve_func(
&self,
field_name: &str,
signature: &Signature,
) -> Result<FuncRef, Error> {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
let index = match field_name {
"ordinary" => ORDINARY_FUNC_INDEX,
"privileged" => {
return Err(Error::Instantiation(
"'priveleged' can be imported only in privileged context".into(),
))
));
}
_ => {
return Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)));
}
};
@ -673,7 +676,8 @@ fn two_envs_one_externals() {
let trusted_instance = ModuleInstance::new(
&trusted_module,
&ImportsBuilder::new().with_resolver("env", &PrivilegedResolver),
).expect("Failed to instantiate module")
)
.expect("Failed to instantiate module")
.assert_no_start();
let untrusted_instance = ModuleInstance::new(
@ -681,7 +685,8 @@ fn two_envs_one_externals() {
&ImportsBuilder::new()
.with_resolver("env", &OrdinaryResolver)
.with_resolver("trusted", &trusted_instance),
).expect("Failed to instantiate module")
)
.expect("Failed to instantiate module")
.assert_no_start();
untrusted_instance
@ -716,7 +721,8 @@ fn dynamically_add_host_func() {
Signature::new(&[][..], Some(ValueType::I32)),
host_func_index as usize,
);
self.table.set(table_index, Some(added_func))
self.table
.set(table_index, Some(added_func))
.map_err(|_| TrapKind::TableAccessOutOfBounds)?;
Ok(Some(RuntimeValue::I32(table_index as i32)))
@ -730,17 +736,14 @@ fn dynamically_add_host_func() {
}
impl ModuleImportResolver for HostExternals {
fn resolve_func(
&self,
field_name: &str,
signature: &Signature,
) -> Result<FuncRef, Error> {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
let index = match field_name {
"add_func" => ADD_FUNC_FUNC_INDEX,
_ => {
return Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)));
}
};
Ok(FuncInstance::alloc_host(signature.clone(), index))
@ -754,9 +757,10 @@ fn dynamically_add_host_func() {
if field_name == "table" {
Ok(self.table.clone())
} else {
Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
}
}
@ -788,7 +792,8 @@ fn dynamically_add_host_func() {
let instance = ModuleInstance::new(
&module,
&ImportsBuilder::new().with_resolver("env", &host_externals),
).expect("Failed to instantiate module")
)
.expect("Failed to instantiate module")
.assert_no_start();
assert_eq!(

View File

@ -1,5 +1,5 @@
use wabt;
use {Module};
use Module;
mod host;
mod wasm;
@ -24,10 +24,16 @@ fn unsigned_to_runtime_value() {
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);
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);
assert_eq!(
RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(),
overflow_i64
);
}
pub fn parse_wat(source: &str) -> Module {

View File

@ -1,10 +1,10 @@
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,
@ -60,12 +60,17 @@ impl ModuleImportResolver for Env {
}
}
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> {
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),
)),
_ => Err(Error::Instantiation(format!(
"env module doesn't provide table '{}'",
field_name
))),
}
}
}
@ -90,10 +95,8 @@ fn interpreter_inc_i32() {
let env = Env::new();
let instance = ModuleInstance::new(
&module,
&ImportsBuilder::new().with_resolver("env", &env),
).expect("Failed to instantiate module")
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
.expect("Failed to instantiate module")
.assert_no_start();
let i32_val = 42;
@ -114,17 +117,14 @@ 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 env = Env::new();
let instance = ModuleInstance::new(
&module,
&ImportsBuilder::new().with_resolver("env", &env),
).expect("Failed to instantiate module")
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");

View File

@ -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].
///
@ -38,7 +39,7 @@ impl Signature {
/// ```
pub fn new<C: Into<Cow<'static, [ValueType]>>>(
params: C,
return_type: Option<ValueType>
return_type: Option<ValueType>,
) -> Signature {
Signature {
params: params.into(),
@ -58,7 +59,12 @@ impl Signature {
pub(crate) fn from_elements(func_type: &FunctionType) -> Signature {
Signature {
params: func_type.params().iter().cloned().map(ValueType::from_elements).collect(),
params: func_type
.params()
.iter()
.cloned()
.map(ValueType::from_elements)
.collect(),
return_type: func_type.return_type().map(ValueType::from_elements),
}
}

View File

@ -1,6 +1,8 @@
#[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)]
@ -47,26 +49,30 @@ impl ModuleContext {
}
pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
let ty_idx = self.func_type_indexes()
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()
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()
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()
let global = self
.globals()
.get(idx as usize)
.ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?;

View File

@ -2,9 +2,7 @@
use alloc::prelude::*;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use core::u32;
use parity_wasm::elements::{
BlockType, Func, FuncBody, Instruction, TableElementType, ValueType,
};
use parity_wasm::elements::{BlockType, Func, FuncBody, Instruction, TableElementType, ValueType};
use validation::context::ModuleContext;
use validation::util::Locals;
@ -1956,4 +1954,3 @@ impl Sink {
self.ins
}
}

View File

@ -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;
@ -158,7 +158,8 @@ pub fn deny_floating_point(module: &Module) -> Result<(), Error> {
if let Some(typ) = types.get(sig.type_ref() as usize) {
match *typ {
Type::Function(ref func) => {
if func.params()
if func
.params()
.iter()
.chain(func.return_type().as_ref())
.any(|&typ| typ == ValueType::F32 || typ == ValueType::F64)
@ -245,32 +246,32 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
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
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",
);
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 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))
Error(format!(
"Function #{} reading/validation error: {}",
index, msg
))
})?;
code_map.push(code);
}
@ -293,9 +294,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
// 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()),
));
return Err(Error(format!("duplicate export {}", export.field())));
}
match *export.internal() {
Internal::Function(function_index) => {
@ -382,10 +381,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
}
}
Ok(ValidatedModule {
module,
code_map,
})
Ok(ValidatedModule { module, code_map })
}
fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
@ -437,8 +433,7 @@ fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<Value
Instruction::I64Const(_) => ValueType::I64,
Instruction::F32Const(_) => ValueType::F32,
Instruction::F64Const(_) => ValueType::F64,
Instruction::GetGlobal(idx) => {
match globals.get(idx as usize) {
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)));
@ -446,12 +441,12 @@ fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<Value
target_global.content_type()
}
None => {
return Err(Error(
format!("Global {} doesn't exists or not yet defined", idx),
))
}
}
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 {

View File

@ -35,7 +35,8 @@ fn limits() {
"core".into(),
"table".into(),
External::Table(TableType::new(min, max)),
)).build();
))
.build();
assert_eq!(validate_module(m).is_ok(), is_valid);
// defined memory
@ -53,7 +54,8 @@ fn limits() {
"core".into(),
"memory".into(),
External::Memory(MemoryType::new(min, max)),
)).build();
))
.build();
assert_eq!(validate_module(m).is_ok(), is_valid);
}
}
@ -64,7 +66,8 @@ fn global_init_const() {
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
)).build();
))
.build();
assert!(validate_module(m).is_ok());
// init expr type differs from declared global type
@ -72,7 +75,8 @@ fn global_init_const() {
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I64, true),
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
)).build();
))
.build();
assert!(validate_module(m).is_err());
}
@ -83,10 +87,12 @@ fn global_init_global() {
"env".into(),
"ext_global".into(),
External::Global(GlobalType::new(ValueType::I32, false)),
)).with_global(GlobalEntry::new(
))
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
)).build();
))
.build();
assert!(validate_module(m).is_ok());
// get_global can reference only previously defined globals
@ -94,7 +100,8 @@ fn global_init_global() {
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
)).build();
))
.build();
assert!(validate_module(m).is_err());
// get_global can reference only const globals
@ -103,10 +110,12 @@ fn global_init_global() {
"env".into(),
"ext_global".into(),
External::Global(GlobalType::new(ValueType::I32, true)),
)).with_global(GlobalEntry::new(
))
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
)).build();
))
.build();
assert!(validate_module(m).is_err());
// get_global in init_expr can only refer to imported globals.
@ -114,10 +123,12 @@ fn global_init_global() {
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, false),
InitExpr::new(vec![Instruction::I32Const(0), Instruction::End]),
)).with_global(GlobalEntry::new(
))
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
)).build();
))
.build();
assert!(validate_module(m).is_err());
}
@ -128,7 +139,8 @@ fn global_init_misc() {
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::I32Const(42)]),
)).build();
))
.build();
assert!(validate_module(m).is_err());
// empty init expr
@ -136,7 +148,8 @@ fn global_init_misc() {
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::End]),
)).build();
))
.build();
assert!(validate_module(m).is_err());
// not an constant opcode used
@ -144,7 +157,8 @@ fn global_init_misc() {
.with_global(GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::Unreachable, Instruction::End]),
)).build();
))
.build();
assert!(validate_module(m).is_err());
}
@ -156,7 +170,8 @@ fn module_limits_validity() {
"core".into(),
"memory".into(),
External::Memory(MemoryType::new(10, None)),
)).memory()
))
.memory()
.with_min(10)
.build()
.build();
@ -168,7 +183,8 @@ fn module_limits_validity() {
"core".into(),
"table".into(),
External::Table(TableType::new(10, None)),
)).table()
))
.table()
.with_min(10)
.build()
.build();
@ -188,7 +204,8 @@ fn funcs() {
.with_instructions(Instructions::new(vec![
Instruction::Call(1),
Instruction::End,
])).build()
]))
.build()
.build()
.function()
.signature()
@ -199,7 +216,8 @@ fn funcs() {
.with_instructions(Instructions::new(vec![
Instruction::Call(0),
Instruction::End,
])).build()
]))
.build()
.build()
.build();
assert!(validate_module(m).is_ok());
@ -213,7 +231,8 @@ fn globals() {
"env".into(),
"ext_global".into(),
External::Global(GlobalType::new(ValueType::I32, false)),
)).build();
))
.build();
assert!(validate_module(m).is_ok());
// import mutable global is invalid.
@ -222,7 +241,8 @@ fn globals() {
"env".into(),
"ext_global".into(),
External::Global(GlobalType::new(ValueType::I32, true)),
)).build();
))
.build();
assert!(validate_module(m).is_err());
}
@ -245,7 +265,8 @@ fn if_else_with_return_type_validation() {
Instruction::Drop,
Instruction::End,
Instruction::End,
])).build()
]))
.build()
.build()
.build();
validate_module(m).unwrap();

View File

@ -22,9 +22,7 @@ impl<'a> Locals<'a> {
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_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
}
Ok(Locals {
@ -125,9 +123,6 @@ mod tests {
Local::new(u32::max_value(), ValueType::I32),
Local::new(1, ValueType::I64),
];
assert_matches!(
Locals::new(&[], &local_groups),
Err(_)
);
assert_matches!(Locals::new(&[], &local_groups), Err(_));
}
}

View File

@ -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);

View File

@ -1,14 +1,16 @@
#![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 {
@ -190,7 +192,8 @@ impl SpecDriver {
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
match name {
Some(name) => self.module(name),
None => self.last_module
None => self
.last_module
.clone()
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
}
@ -301,7 +304,8 @@ fn run_action(
"Expected program to have loaded module {:?}",
module
));
let vec_args = args.iter()
let vec_args = args
.iter()
.cloned()
.map(spec_to_runtime_value)
.collect::<Vec<_>>();
@ -345,9 +349,12 @@ fn try_spec(name: &str) -> Result<(), Error> {
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");
spec_file
.read_to_end(&mut spec_source)
.expect("Can't read file");
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)).expect("Can't read spec script");
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name))
.expect("Can't read spec script");
let mut errors = vec![];
while let Some(Command { kind, line }) = parser.next()? {
@ -414,12 +421,16 @@ fn try_spec(name: &str) -> Result<(), Error> {
Ok(result) => {
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
match actual_result {
RuntimeValue::F32(val) => if !val.is_nan() {
RuntimeValue::F32(val) => {
if !val.is_nan() {
panic!("Expected nan value, got {:?}", val)
},
RuntimeValue::F64(val) => if !val.is_nan() {
}
}
RuntimeValue::F64(val) => {
if !val.is_nan() {
panic!("Expected nan value, got {:?}", val)
},
}
}
val @ _ => {
panic!("Expected action to return float value, got {:?}", val)
}
@ -435,7 +446,7 @@ fn try_spec(name: &str) -> Result<(), Error> {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
Err(_e) => {},
Err(_e) => {}
}
}
CommandKind::AssertTrap { action, .. } => {
@ -456,13 +467,13 @@ fn try_spec(name: &str) -> Result<(), Error> {
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) => {},
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) => {},
Err(_e) => {}
}
}
CommandKind::Register { name, as_name, .. } => {

View File

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