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

View File

@ -4,7 +4,7 @@ extern crate wasmi;
use std::env::args; use std::env::args;
use std::fs::File; use std::fs::File;
use wasmi::{ModuleInstance, NopExternals, RuntimeValue, ImportsBuilder, Module}; use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue};
fn load_from_file(filename: &str) -> Module { fn load_from_file(filename: &str) -> Module {
use std::io::prelude::*; use std::io::prelude::*;
@ -40,5 +40,8 @@ fn main() {
let argument: i32 = args[2].parse().expect("Integer argument required"); let argument: i32 = args[2].parse().expect("Integer argument required");
// "_call" export of function to be executed with an i32 argument and prints the result of execution // "_call" export of function to be executed with an i32 argument and prints the result of execution
println!("Result: {:?}", main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals)); println!(
"Result: {:?}",
main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals)
);
} }

View File

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

View File

@ -1,14 +1,13 @@
extern crate wasmi;
extern crate parity_wasm; extern crate parity_wasm;
extern crate wasmi;
use std::env; use std::env;
use std::fmt; use std::fmt;
use std::fs::File; use std::fs::File;
use wasmi::{ use wasmi::{
Error as InterpreterError, ModuleInstance, ModuleRef, Error as InterpreterError, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
Externals, RuntimeValue, FuncRef, ModuleImportResolver, ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, Trap,
FuncInstance, HostError, ImportsBuilder, Signature, ValueType, ValueType,
RuntimeArgs, Trap,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -64,9 +63,7 @@ mod tictactoe {
impl Game { impl Game {
pub fn new() -> Game { pub fn new() -> Game {
Game { Game { board: [None; 9] }
board: [None; 9],
}
} }
pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> { pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
@ -96,12 +93,10 @@ mod tictactoe {
(0, 1, 2), (0, 1, 2),
(3, 4, 5), (3, 4, 5),
(6, 7, 8), (6, 7, 8),
// Columns // Columns
(0, 3, 6), (0, 3, 6),
(1, 4, 7), (1, 4, 7),
(2, 5, 8), (2, 5, 8),
// Diagonals // Diagonals
(0, 4, 8), (0, 4, 8),
(2, 4, 6), (2, 4, 6),
@ -161,7 +156,7 @@ impl<'a> Externals for Runtime<'a> {
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?); let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
Ok(Some(val.into())) Ok(Some(val.into()))
} }
_ => panic!("unknown function index") _ => panic!("unknown function index"),
} }
} }
} }
@ -175,15 +170,20 @@ impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
_signature: &Signature, _signature: &Signature,
) -> Result<FuncRef, InterpreterError> { ) -> Result<FuncRef, InterpreterError> {
let func_ref = match field_name { let func_ref = match field_name {
"set" => { "set" => FuncInstance::alloc_host(
FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], None), SET_FUNC_INDEX) Signature::new(&[ValueType::I32][..], None),
}, SET_FUNC_INDEX,
"get" => FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], Some(ValueType::I32)), GET_FUNC_INDEX), ),
_ => return Err( "get" => FuncInstance::alloc_host(
InterpreterError::Function( Signature::new(&[ValueType::I32][..], Some(ValueType::I32)),
format!("host module doesn't export function with name {}", field_name) GET_FUNC_INDEX,
) ),
) _ => {
return Err(InterpreterError::Function(format!(
"host module doesn't export function with name {}",
field_name
)));
}
}; };
Ok(func_ref) Ok(func_ref)
} }
@ -201,8 +201,7 @@ fn instantiate(path: &str) -> Result<ModuleRef, Error> {
let mut imports = ImportsBuilder::new(); let mut imports = ImportsBuilder::new();
imports.push_resolver("env", &RuntimeModuleImportResolver); imports.push_resolver("env", &RuntimeModuleImportResolver);
let instance = ModuleInstance::new(&module, &imports)? let instance = ModuleInstance::new(&module, &imports)?.assert_no_start();
.assert_no_start();
Ok(instance) Ok(instance)
} }

View File

@ -5,12 +5,12 @@ extern crate wasmi;
use std::env::args; use std::env::args;
use std::fs::File; use std::fs::File;
use wasmi::{
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef,
ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, Signature,
TableDescriptor, TableInstance, TableRef};
use wasmi::memory_units::*; use wasmi::memory_units::*;
use wasmi::{
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder,
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance,
NopExternals, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
};
fn load_from_file(filename: &str) -> Module { fn load_from_file(filename: &str) -> Module {
use std::io::prelude::*; use std::io::prelude::*;
@ -46,7 +46,8 @@ impl ModuleImportResolver for ResolveAll {
Ok(MemoryInstance::alloc( Ok(MemoryInstance::alloc(
Pages(memory_type.initial() as usize), Pages(memory_type.initial() as usize),
memory_type.maximum().map(|m| Pages(m as usize)), memory_type.maximum().map(|m| Pages(m as usize)),
).unwrap()) )
.unwrap())
} }
fn resolve_table( fn resolve_table(
@ -75,7 +76,8 @@ fn main() {
.with_resolver("global.Math", &ResolveAll) .with_resolver("global.Math", &ResolveAll)
.with_resolver("asm2wasm", &ResolveAll) .with_resolver("asm2wasm", &ResolveAll)
.with_resolver("spectest", &ResolveAll), .with_resolver("spectest", &ResolveAll),
).expect("Failed to instantiate module") )
.expect("Failed to instantiate module")
.run_start(&mut NopExternals) .run_start(&mut NopExternals)
.expect("Failed to run start function in module"); .expect("Failed to run start function in module");
} }

View File

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

View File

@ -1,9 +1,9 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use core::fmt;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error; use std::error;
use core::fmt;
#[derive(Debug)] #[derive(Debug)]
pub struct Error(String); pub struct Error(String);
@ -23,18 +23,24 @@ impl error::Error for Error {
/// Stack with limit. /// Stack with limit.
#[derive(Debug)] #[derive(Debug)]
pub struct StackWithLimit<T> where T: Clone { pub struct StackWithLimit<T>
where
T: Clone,
{
/// Stack values. /// Stack values.
values: Vec<T>, values: Vec<T>,
/// Stack limit (maximal stack len). /// Stack limit (maximal stack len).
limit: usize, limit: usize,
} }
impl<T> StackWithLimit<T> where T: Clone { impl<T> StackWithLimit<T>
where
T: Clone,
{
pub fn with_limit(limit: usize) -> Self { pub fn with_limit(limit: usize) -> Self {
StackWithLimit { StackWithLimit {
values: Vec::new(), 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> { pub fn get(&self, index: usize) -> Result<&T, Error> {
if index >= self.values.len() { if index >= self.values.len() {
return Err(Error(format!("trying to get value at position {} on stack of size {}", index, self.values.len()))); return Err(Error(format!(
"trying to get value at position {} on stack of size {}",
index,
self.values.len()
)));
} }
Ok(self.values.get(self.values.len() - 1 - index).expect("checked couple of lines above")) Ok(self
.values
.get(self.values.len() - 1 - index)
.expect("checked couple of lines above"))
} }
pub fn push(&mut self, value: T) -> Result<(), Error> { pub fn push(&mut self, value: T) -> Result<(), Error> {

View File

@ -2,14 +2,14 @@
use alloc::prelude::*; use alloc::prelude::*;
use alloc::rc::{Rc, Weak}; use alloc::rc::{Rc, Weak};
use core::fmt; use core::fmt;
use parity_wasm::elements::Local;
use {Trap, Signature};
use host::Externals; use host::Externals;
use runner::{check_function_args, Interpreter, InterpreterState};
use value::RuntimeValue;
use types::ValueType;
use module::ModuleInstance;
use isa; use isa;
use module::ModuleInstance;
use parity_wasm::elements::Local;
use runner::{check_function_args, Interpreter, InterpreterState};
use types::ValueType;
use value::RuntimeValue;
use {Signature, Trap};
/// Reference to a function (See [`FuncInstance`] for details). /// Reference to a function (See [`FuncInstance`] for details).
/// ///
@ -58,17 +58,10 @@ pub(crate) enum FuncInstanceInternal {
impl fmt::Debug for FuncInstance { impl fmt::Debug for FuncInstance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.as_internal() { match self.as_internal() {
&FuncInstanceInternal::Internal { &FuncInstanceInternal::Internal { ref signature, .. } => {
ref signature,
..
} => {
// We can't write description of self.module here, because it generate // We can't write description of self.module here, because it generate
// debug string for function instances and this will lead to infinite loop. // debug string for function instances and this will lead to infinite loop.
write!( write!(f, "Internal {{ signature={:?} }}", signature,)
f,
"Internal {{ signature={:?} }}",
signature,
)
} }
&FuncInstanceInternal::Host { ref signature, .. } => { &FuncInstanceInternal::Host { ref signature, .. } => {
write!(f, "Host {{ signature={:?} }}", signature) write!(f, "Host {{ signature={:?} }}", signature)
@ -186,15 +179,13 @@ impl FuncInstance {
FuncInstanceInternal::Host { FuncInstanceInternal::Host {
ref host_func_index, ref host_func_index,
.. ..
} => { } => Ok(FuncInvocation {
Ok(FuncInvocation {
kind: FuncInvocationKind::Host { kind: FuncInvocationKind::Host {
args, args,
host_func_index: *host_func_index, host_func_index: *host_func_index,
finished: false, finished: false,
}, },
}) }),
},
} }
} }
} }
@ -239,7 +230,7 @@ enum FuncInvocationKind<'args> {
Host { Host {
args: &'args [RuntimeValue], args: &'args [RuntimeValue],
host_func_index: usize, 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. /// If the invocation is resumable, the expected return value type to be feed back in.
pub fn resumable_value_type(&self) -> Option<ValueType> { pub fn resumable_value_type(&self) -> Option<ValueType> {
match &self.kind { match &self.kind {
&FuncInvocationKind::Internal(ref interpreter) => { &FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
match interpreter.state() {
&InterpreterState::Resumable(ref value_type) => value_type.clone(), &InterpreterState::Resumable(ref value_type) => value_type.clone(),
_ => None, _ => None,
}
}, },
&FuncInvocationKind::Host { .. } => None, &FuncInvocationKind::Host { .. } => None,
} }
} }
/// Start the invocation execution. /// Start the invocation execution.
pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> { pub fn start_execution<'externals, E: Externals + 'externals>(
&mut self,
externals: &'externals mut E,
) -> Result<Option<RuntimeValue>, ResumableError> {
match self.kind { match self.kind {
FuncInvocationKind::Internal(ref mut interpreter) => { FuncInvocationKind::Internal(ref mut interpreter) => {
if interpreter.state() != &InterpreterState::Initialized { if interpreter.state() != &InterpreterState::Initialized {
return Err(ResumableError::AlreadyStarted); return Err(ResumableError::AlreadyStarted);
} }
Ok(interpreter.start_execution(externals)?) 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 { if *finished {
return Err(ResumableError::AlreadyStarted); return Err(ResumableError::AlreadyStarted);
} }
*finished = true; *finished = true;
Ok(externals.invoke_index(*host_func_index, args.clone().into())?) 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 /// [`resumable_value_type`]: #method.resumable_value_type
/// [`is_resumable`]: #method.is_resumable /// [`is_resumable`]: #method.is_resumable
pub fn resume_execution<'externals, E: Externals + 'externals>(&mut self, return_val: Option<RuntimeValue>, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> { pub fn resume_execution<'externals, E: Externals + 'externals>(
&mut self,
return_val: Option<RuntimeValue>,
externals: &'externals mut E,
) -> Result<Option<RuntimeValue>, ResumableError> {
match self.kind { match self.kind {
FuncInvocationKind::Internal(ref mut interpreter) => { FuncInvocationKind::Internal(ref mut interpreter) => {
if !interpreter.state().is_resumable() { if !interpreter.state().is_resumable() {
return Err(ResumableError::AlreadyStarted); return Err(ResumableError::AlreadyStarted);
} }
Ok(interpreter.resume_execution(return_val, externals)?) Ok(interpreter.resume_execution(return_val, externals)?)
}, }
FuncInvocationKind::Host { .. } => { FuncInvocationKind::Host { .. } => {
return Err(ResumableError::NotResumable); return Err(ResumableError::NotResumable);
}, }
} }
} }
} }

View File

@ -1,9 +1,9 @@
use alloc::rc::Rc; use alloc::rc::Rc;
use core::cell::Cell; use core::cell::Cell;
use parity_wasm::elements::ValueType as EValueType;
use types::ValueType;
use value::RuntimeValue; use value::RuntimeValue;
use Error; use Error;
use types::ValueType;
use parity_wasm::elements::{ValueType as EValueType};
/// Reference to a global variable (See [`GlobalInstance`] for details). /// Reference to a global variable (See [`GlobalInstance`] for details).
/// ///
@ -57,7 +57,9 @@ impl GlobalInstance {
/// type of `val` doesn't match global's type. /// type of `val` doesn't match global's type.
pub fn set(&self, val: RuntimeValue) -> Result<(), Error> { pub fn set(&self, val: RuntimeValue) -> Result<(), Error> {
if !self.mutable { if !self.mutable {
return Err(Error::Global("Attempt to change an immutable variable".into())); return Err(Error::Global(
"Attempt to change an immutable variable".into(),
));
} }
if self.value_type() != val.value_type() { if self.value_type() != val.value_type() {
return Err(Error::Global("Attempt to change variable type".into())); return Err(Error::Global("Attempt to change variable type".into()));

View File

@ -1,6 +1,6 @@
use core::any::TypeId; use core::any::TypeId;
use value::{RuntimeValue, FromRuntimeValue}; use value::{FromRuntimeValue, RuntimeValue};
use {TrapKind, Trap}; use {Trap, TrapKind};
/// Wrapper around slice of [`RuntimeValue`] for using it /// Wrapper around slice of [`RuntimeValue`] for using it
/// as an argument list conveniently. /// as an argument list conveniently.
@ -27,8 +27,14 @@ impl<'a> RuntimeArgs<'a> {
/// # Errors /// # Errors
/// ///
/// Returns `Err` if cast is invalid or not enough arguments. /// Returns `Err` if cast is invalid or not enough arguments.
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap> where T: FromRuntimeValue { pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap>
Ok(self.nth_value_checked(idx)?.try_into().ok_or_else(|| TrapKind::UnexpectedSignature)?) where
T: FromRuntimeValue,
{
Ok(self
.nth_value_checked(idx)?
.try_into()
.ok_or_else(|| TrapKind::UnexpectedSignature)?)
} }
/// Extract argument as a [`RuntimeValue`] by index `idx`. /// Extract argument as a [`RuntimeValue`] by index `idx`.
@ -50,7 +56,10 @@ impl<'a> RuntimeArgs<'a> {
/// # Panics /// # Panics
/// ///
/// Panics if cast is invalid or not enough arguments. /// Panics if cast is invalid or not enough arguments.
pub fn nth<T>(&self, idx: usize) -> T where T: FromRuntimeValue { pub fn nth<T>(&self, idx: usize) -> T
where
T: FromRuntimeValue,
{
let value = self.nth_value_checked(idx).expect("Invalid argument index"); let value = self.nth_value_checked(idx).expect("Invalid argument index");
value.try_into().expect("Unexpected argument type") value.try_into().expect("Unexpected argument type")
} }
@ -231,8 +240,8 @@ impl Externals for NopExternals {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{HostError, RuntimeArgs};
use value::RuntimeValue; use value::RuntimeValue;
use super::{RuntimeArgs, HostError};
#[test] #[test]
fn i32_runtime_args() { fn i32_runtime_args() {
@ -248,6 +257,5 @@ mod tests {
} }
// Tests that `HostError` trait is object safe. // Tests that `HostError` trait is object safe.
fn _host_error_is_object_safe(_: &HostError) { fn _host_error_is_object_safe(_: &HostError) {}
}
} }

View File

@ -1,20 +1,19 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use hashmap_core::HashMap; use hashmap_core::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
use func::FuncRef;
use global::GlobalRef; use global::GlobalRef;
use memory::MemoryRef; use memory::MemoryRef;
use func::FuncRef;
use table::TableRef;
use module::ModuleRef; use module::ModuleRef;
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor}; use table::TableRef;
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
use {Error, Signature}; use {Error, Signature};
/// Resolver of a module's dependencies. /// Resolver of a module's dependencies.
/// ///
/// A module have dependencies in a form of a list of imports (i.e. /// A module have dependencies in a form of a list of imports (i.e.
@ -27,7 +26,6 @@ use {Error, Signature};
/// ///
/// [`ImportsBuilder`]: struct.ImportsBuilder.html /// [`ImportsBuilder`]: struct.ImportsBuilder.html
pub trait ImportResolver { pub trait ImportResolver {
/// Resolve a function. /// Resolve a function.
/// ///
/// Returned function should match given `signature`, i.e. all parameter types and return value should have exact match. /// 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> { impl<'a> ImportsBuilder<'a> {
/// Create an empty `ImportsBuilder`. /// Create an empty `ImportsBuilder`.
pub fn new() -> ImportsBuilder<'a> { pub fn new() -> ImportsBuilder<'a> {
ImportsBuilder { modules: HashMap::new() } ImportsBuilder {
modules: HashMap::new(),
}
} }
/// Register an resolver by a name. /// Register an resolver by a name.
@ -152,9 +152,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
field_name: &str, field_name: &str,
signature: &Signature, signature: &Signature,
) -> Result<FuncRef, Error> { ) -> Result<FuncRef, Error> {
self.resolver(module_name).ok_or_else(|| self.resolver(module_name)
Error::Instantiation(format!("Module {} not found", module_name)) .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
)?.resolve_func(field_name, signature) .resolve_func(field_name, signature)
} }
fn resolve_global( fn resolve_global(
@ -163,9 +163,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
field_name: &str, field_name: &str,
global_type: &GlobalDescriptor, global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> { ) -> Result<GlobalRef, Error> {
self.resolver(module_name).ok_or_else(|| self.resolver(module_name)
Error::Instantiation(format!("Module {} not found", module_name)) .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
)?.resolve_global(field_name, global_type) .resolve_global(field_name, global_type)
} }
fn resolve_memory( fn resolve_memory(
@ -174,9 +174,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
field_name: &str, field_name: &str,
memory_type: &MemoryDescriptor, memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> { ) -> Result<MemoryRef, Error> {
self.resolver(module_name).ok_or_else(|| self.resolver(module_name)
Error::Instantiation(format!("Module {} not found", module_name)) .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
)?.resolve_memory(field_name, memory_type) .resolve_memory(field_name, memory_type)
} }
fn resolve_table( fn resolve_table(
@ -185,9 +185,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
field_name: &str, field_name: &str,
table_type: &TableDescriptor, table_type: &TableDescriptor,
) -> Result<TableRef, Error> { ) -> Result<TableRef, Error> {
self.resolver(module_name).ok_or_else(|| self.resolver(module_name)
Error::Instantiation(format!("Module {} not found", module_name)) .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
)?.resolve_table(field_name, table_type) .resolve_table(field_name, table_type)
} }
} }
@ -200,14 +200,11 @@ pub trait ModuleImportResolver {
/// See [`ImportResolver::resolve_func`] for details. /// See [`ImportResolver::resolve_func`] for details.
/// ///
/// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func /// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
fn resolve_func( fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
&self, Err(Error::Instantiation(format!(
field_name: &str, "Export {} not found",
_signature: &Signature, field_name
) -> Result<FuncRef, Error> { )))
Err(Error::Instantiation(
format!("Export {} not found", field_name),
))
} }
/// Resolve a global variable. /// Resolve a global variable.
@ -220,9 +217,10 @@ pub trait ModuleImportResolver {
field_name: &str, field_name: &str,
_global_type: &GlobalDescriptor, _global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> { ) -> Result<GlobalRef, Error> {
Err(Error::Instantiation( Err(Error::Instantiation(format!(
format!("Export {} not found", field_name), "Export {} not found",
)) field_name
)))
} }
/// Resolve a memory. /// Resolve a memory.
@ -235,9 +233,10 @@ pub trait ModuleImportResolver {
field_name: &str, field_name: &str,
_memory_type: &MemoryDescriptor, _memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> { ) -> Result<MemoryRef, Error> {
Err(Error::Instantiation( Err(Error::Instantiation(format!(
format!("Export {} not found", field_name), "Export {} not found",
)) field_name
)))
} }
/// Resolve a table. /// Resolve a table.
@ -250,22 +249,18 @@ pub trait ModuleImportResolver {
field_name: &str, field_name: &str,
_table_type: &TableDescriptor, _table_type: &TableDescriptor,
) -> Result<TableRef, Error> { ) -> Result<TableRef, Error> {
Err(Error::Instantiation( Err(Error::Instantiation(format!(
format!("Export {} not found", field_name), "Export {} not found",
)) field_name
)))
} }
} }
impl ModuleImportResolver for ModuleRef { impl ModuleImportResolver for ModuleRef {
fn resolve_func( fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
&self, Ok(self
field_name: &str, .export_by_name(field_name)
_signature: &Signature, .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
) -> Result<FuncRef, Error> {
Ok(self.export_by_name(field_name)
.ok_or_else(|| {
Error::Instantiation(format!("Export {} not found", field_name))
})?
.as_func() .as_func()
.cloned() .cloned()
.ok_or_else(|| { .ok_or_else(|| {
@ -278,10 +273,9 @@ impl ModuleImportResolver for ModuleRef {
field_name: &str, field_name: &str,
_global_type: &GlobalDescriptor, _global_type: &GlobalDescriptor,
) -> Result<GlobalRef, Error> { ) -> Result<GlobalRef, Error> {
Ok(self.export_by_name(field_name) Ok(self
.ok_or_else(|| { .export_by_name(field_name)
Error::Instantiation(format!("Export {} not found", field_name)) .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
})?
.as_global() .as_global()
.cloned() .cloned()
.ok_or_else(|| { .ok_or_else(|| {
@ -294,10 +288,9 @@ impl ModuleImportResolver for ModuleRef {
field_name: &str, field_name: &str,
_memory_type: &MemoryDescriptor, _memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, Error> { ) -> Result<MemoryRef, Error> {
Ok(self.export_by_name(field_name) Ok(self
.ok_or_else(|| { .export_by_name(field_name)
Error::Instantiation(format!("Export {} not found", field_name)) .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
})?
.as_memory() .as_memory()
.cloned() .cloned()
.ok_or_else(|| { .ok_or_else(|| {
@ -310,14 +303,11 @@ impl ModuleImportResolver for ModuleRef {
field_name: &str, field_name: &str,
_table_type: &TableDescriptor, _table_type: &TableDescriptor,
) -> Result<TableRef, Error> { ) -> Result<TableRef, Error> {
Ok(self.export_by_name(field_name) Ok(self
.ok_or_else(|| { .export_by_name(field_name)
Error::Instantiation(format!("Export {} not found", field_name)) .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
})?
.as_table() .as_table()
.cloned() .cloned()
.ok_or_else(|| { .ok_or_else(|| Error::Instantiation(format!("Export {} is not a table", field_name)))?)
Error::Instantiation(format!("Export {} is not a table", field_name))
})?)
} }
} }

View File

@ -95,9 +95,7 @@
//! ``` //! ```
#![warn(missing_docs)] #![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
//// alloc is required in no_std //// alloc is required in no_std
#![cfg_attr(not(feature = "std"), feature(alloc))] #![cfg_attr(not(feature = "std"), feature(alloc))]
@ -117,11 +115,11 @@ extern crate wabt;
#[macro_use] #[macro_use]
extern crate assert_matches; extern crate assert_matches;
extern crate parity_wasm;
extern crate byteorder; extern crate byteorder;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
extern crate hashmap_core; extern crate hashmap_core;
extern crate memory_units as memory_units_crate; extern crate memory_units as memory_units_crate;
extern crate parity_wasm;
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
@ -292,7 +290,7 @@ impl Error {
Error::Trap(ref trap) => match *trap.kind() { Error::Trap(ref trap) => match *trap.kind() {
TrapKind::Host(ref host_err) => Some(&**host_err), TrapKind::Host(ref host_err) => Some(&**host_err),
_ => None, _ => None,
} },
_ => None, _ => None,
} }
} }
@ -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 { fn from(e: U) -> Self {
Error::Host(Box::new(e)) 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 { fn from(e: U) -> Self {
Trap::new(TrapKind::Host(Box::new(e))) Trap::new(TrapKind::Host(Box::new(e)))
} }
@ -377,38 +381,38 @@ impl From<validation::Error> for Error {
} }
} }
mod validation;
mod common; mod common;
mod memory; mod func;
mod module; mod global;
mod runner;
mod table;
mod value;
mod host; mod host;
mod imports; mod imports;
mod global;
mod func;
mod types;
mod isa; mod isa;
mod memory;
mod module;
pub mod nan_preserving_float; pub mod nan_preserving_float;
mod runner;
mod table;
mod types;
mod validation;
mod value;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE}; pub use self::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError};
pub use self::table::{TableInstance, TableRef};
pub use self::value::{FromRuntimeValue, RuntimeValue, LittleEndianConvert, Error as ValueError};
pub use self::host::{Externals, NopExternals, HostError, RuntimeArgs};
pub use self::imports::{ModuleImportResolver, ImportResolver, ImportsBuilder};
pub use self::module::{ModuleInstance, ModuleRef, ExternVal, NotStartedModuleRef};
pub use self::global::{GlobalInstance, GlobalRef}; pub use self::global::{GlobalInstance, GlobalRef};
pub use self::func::{FuncInstance, FuncRef, FuncInvocation, ResumableError}; pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs};
pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor}; pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver};
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef};
pub use self::table::{TableInstance, TableRef};
pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType};
pub use self::value::{Error as ValueError, FromRuntimeValue, LittleEndianConvert, RuntimeValue};
/// WebAssembly-specific sizes and units. /// WebAssembly-specific sizes and units.
pub mod memory_units { pub mod memory_units {
pub use memory_units_crate::wasm32::*; pub use memory_units_crate::wasm32::*;
pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of}; pub use memory_units_crate::{size_of, ByteSize, Bytes, RoundUpTo};
} }
/// Deserialized module prepared for instantiation. /// Deserialized module prepared for instantiation.
@ -452,15 +456,9 @@ impl Module {
/// ``` /// ```
pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> { pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> {
use validation::{validate_module, ValidatedModule}; use validation::{validate_module, ValidatedModule};
let ValidatedModule { let ValidatedModule { code_map, module } = validate_module(module)?;
code_map,
module,
} = validate_module(module)?;
Ok(Module { Ok(Module { code_map, module })
code_map,
module,
})
} }
/// Fail if the module contains any floating-point operations /// Fail if the module contains any floating-point operations

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +1,13 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use alloc::rc::Rc; use alloc::rc::Rc;
use core::u32;
use core::fmt;
use core::cell::RefCell; use core::cell::RefCell;
use parity_wasm::elements::ResizableLimits; use core::fmt;
use Error; use core::u32;
use func::FuncRef; use func::FuncRef;
use module::check_limits; use module::check_limits;
use parity_wasm::elements::ResizableLimits;
use Error;
/// Reference to a table (See [`TableInstance`] for details). /// Reference to a table (See [`TableInstance`] for details).
/// ///
@ -106,7 +106,9 @@ impl TableInstance {
pub fn grow(&self, by: u32) -> Result<(), Error> { pub fn grow(&self, by: u32) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
let maximum_size = self.maximum_size().unwrap_or(u32::MAX); let maximum_size = self.maximum_size().unwrap_or(u32::MAX);
let new_size = self.current_size().checked_add(by) let new_size = self
.current_size()
.checked_add(by)
.and_then(|new_size| { .and_then(|new_size| {
if maximum_size < new_size { if maximum_size < new_size {
None None
@ -114,13 +116,13 @@ impl TableInstance {
Some(new_size) Some(new_size)
} }
}) })
.ok_or_else(|| .ok_or_else(|| {
Error::Table(format!( Error::Table(format!(
"Trying to grow table by {} items when there are already {} items", "Trying to grow table by {} items when there are already {} items",
by, by,
self.current_size(), self.current_size(),
)) ))
)?; })?;
buffer.resize(new_size as usize, None); buffer.resize(new_size as usize, None);
Ok(()) Ok(())
} }
@ -129,13 +131,12 @@ impl TableInstance {
pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> { pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> {
let buffer = self.buffer.borrow(); let buffer = self.buffer.borrow();
let buffer_len = buffer.len(); let buffer_len = buffer.len();
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| {
Error::Table(format!( Error::Table(format!(
"trying to read table item with index {} when there are only {} items", "trying to read table item with index {} when there are only {} items",
offset, offset, buffer_len
buffer_len ))
)), })?;
)?;
Ok(table_elem) Ok(table_elem)
} }
@ -143,13 +144,12 @@ impl TableInstance {
pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> { pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> {
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
let buffer_len = buffer.len(); let buffer_len = buffer.len();
let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| {
Error::Table(format!( Error::Table(format!(
"trying to update table item with index {} when there are only {} items", "trying to update table item with index {} when there are only {} items",
offset, offset, buffer_len
buffer_len
)) ))
)?; })?;
*table_elem = value; *table_elem = value;
Ok(()) Ok(())
} }

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

View File

@ -1,5 +1,5 @@
use wabt; use wabt;
use {Module}; use Module;
mod host; mod host;
mod wasm; mod wasm;
@ -24,10 +24,16 @@ fn unsigned_to_runtime_value() {
use super::RuntimeValue; use super::RuntimeValue;
let overflow_i32: u32 = ::core::i32::MAX as u32 + 1; let overflow_i32: u32 = ::core::i32::MAX as u32 + 1;
assert_eq!(RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(), overflow_i32); assert_eq!(
RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(),
overflow_i32
);
let overflow_i64: u64 = ::core::i64::MAX as u64 + 1; let overflow_i64: u64 = ::core::i64::MAX as u64 + 1;
assert_eq!(RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(), overflow_i64); assert_eq!(
RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(),
overflow_i64
);
} }
pub fn parse_wat(source: &str) -> Module { pub fn parse_wat(source: &str) -> Module {

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 memory_units::Pages;
use std::fs::File; use std::fs::File;
use {
Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals,
RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
};
struct Env { struct Env {
table_base: GlobalRef, table_base: GlobalRef,
@ -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 { match field_name {
"table" => Ok(self.table.clone()), "table" => Ok(self.table.clone()),
_ => Err(Error::Instantiation( _ => Err(Error::Instantiation(format!(
format!("env module doesn't provide table '{}'", field_name), "env module doesn't provide table '{}'",
)), field_name
))),
} }
} }
} }
@ -90,10 +95,8 @@ fn interpreter_inc_i32() {
let env = Env::new(); let env = Env::new();
let instance = ModuleInstance::new( let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
&module, .expect("Failed to instantiate module")
&ImportsBuilder::new().with_resolver("env", &env),
).expect("Failed to instantiate module")
.assert_no_start(); .assert_no_start();
let i32_val = 42; let i32_val = 42;
@ -114,17 +117,14 @@ fn interpreter_accumulate_u8() {
// The WASM file containing the module and function // The WASM file containing the module and function
const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast"; const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast";
// The octet sequence being accumulated // The octet sequence being accumulated
const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1]; const BUF: &[u8] = &[9, 8, 7, 6, 5, 4, 3, 2, 1];
// Load the module-structure from wasm-file and add to program // Load the module-structure from wasm-file and add to program
let module = load_from_file(WASM_FILE); let module = load_from_file(WASM_FILE);
let env = Env::new(); let env = Env::new();
let instance = ModuleInstance::new( let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
&module, .expect("Failed to instantiate module")
&ImportsBuilder::new().with_resolver("env", &env),
).expect("Failed to instantiate module")
.assert_no_start(); .assert_no_start();
let env_memory = env.memory.clone(); let env_memory = env.memory.clone();
@ -134,7 +134,10 @@ fn interpreter_accumulate_u8() {
let _ = env_memory.set(offset, BUF); let _ = env_memory.set(offset, BUF);
// Set up the function argument list and invoke the function // Set up the function argument list and invoke the function
let args = &[RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)]; let args = &[
RuntimeValue::I32(BUF.len() as i32),
RuntimeValue::I32(offset as i32),
];
let retval = instance let retval = instance
.invoke_export(FUNCTION_NAME, args, &mut NopExternals) .invoke_export(FUNCTION_NAME, args, &mut NopExternals)
.expect("Failed to execute function"); .expect("Failed to execute function");

View File

@ -1,7 +1,8 @@
use alloc::borrow::Cow; use alloc::borrow::Cow;
use parity_wasm::elements::{ use parity_wasm::elements::{
FunctionType, ValueType as EValueType, GlobalType, TableType, MemoryType}; FunctionType, GlobalType, MemoryType, TableType, ValueType as EValueType,
};
/// Signature of a [function]. /// Signature of a [function].
/// ///
@ -38,7 +39,7 @@ impl Signature {
/// ``` /// ```
pub fn new<C: Into<Cow<'static, [ValueType]>>>( pub fn new<C: Into<Cow<'static, [ValueType]>>>(
params: C, params: C,
return_type: Option<ValueType> return_type: Option<ValueType>,
) -> Signature { ) -> Signature {
Signature { Signature {
params: params.into(), params: params.into(),
@ -58,7 +59,12 @@ impl Signature {
pub(crate) fn from_elements(func_type: &FunctionType) -> Signature { pub(crate) fn from_elements(func_type: &FunctionType) -> Signature {
Signature { Signature {
params: func_type.params().iter().cloned().map(ValueType::from_elements).collect(), params: func_type
.params()
.iter()
.cloned()
.map(ValueType::from_elements)
.collect(),
return_type: func_type.return_type().map(ValueType::from_elements), return_type: func_type.return_type().map(ValueType::from_elements),
} }
} }

View File

@ -1,6 +1,8 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use parity_wasm::elements::{MemoryType, TableType, GlobalType, BlockType, ValueType, FunctionType}; use parity_wasm::elements::{
BlockType, FunctionType, GlobalType, MemoryType, TableType, ValueType,
};
use validation::Error; use validation::Error;
#[derive(Default, Debug)] #[derive(Default, Debug)]
@ -47,26 +49,30 @@ impl ModuleContext {
} }
pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
let ty_idx = self.func_type_indexes() let ty_idx = self
.func_type_indexes()
.get(idx as usize) .get(idx as usize)
.ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?; .ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?;
self.require_function_type(*ty_idx) self.require_function_type(*ty_idx)
} }
pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
let ty = self.types() let ty = self
.types()
.get(idx as usize) .get(idx as usize)
.ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?; .ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?;
let params = ty.params(); let params = ty.params();
let return_ty = ty.return_type() let return_ty = ty
.return_type()
.map(BlockType::Value) .map(BlockType::Value)
.unwrap_or(BlockType::NoResult); .unwrap_or(BlockType::NoResult);
Ok((params, return_ty)) Ok((params, return_ty))
} }
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<&GlobalType, Error> { pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<&GlobalType, Error> {
let global = self.globals() let global = self
.globals()
.get(idx as usize) .get(idx as usize)
.ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?; .ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?;

View File

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

View File

@ -1,23 +1,23 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use core::fmt;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error; use std::error;
use core::fmt;
#[cfg(feature = "std")]
use std::collections::HashSet;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use hashmap_core::HashSet; use hashmap_core::HashSet;
#[cfg(feature = "std")]
use std::collections::HashSet;
use parity_wasm::elements::{
BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction,
ResizableLimits, TableType, ValueType, InitExpr, Type,
};
use common::stack;
use self::context::ModuleContextBuilder; use self::context::ModuleContextBuilder;
use self::func::FunctionReader; use self::func::FunctionReader;
use memory_units::Pages; use common::stack;
use isa; use isa;
use memory_units::Pages;
use parity_wasm::elements::{
BlockType, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, MemoryType,
Module, ResizableLimits, TableType, Type, ValueType,
};
mod context; mod context;
mod func; mod func;
@ -158,7 +158,8 @@ pub fn deny_floating_point(module: &Module) -> Result<(), Error> {
if let Some(typ) = types.get(sig.type_ref() as usize) { if let Some(typ) = types.get(sig.type_ref() as usize) {
match *typ { match *typ {
Type::Function(ref func) => { Type::Function(ref func) => {
if func.params() if func
.params()
.iter() .iter()
.chain(func.return_type().as_ref()) .chain(func.return_type().as_ref())
.any(|&typ| typ == ValueType::F32 || typ == ValueType::F64) .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 { if function_section_len != code_section_len {
return Err(Error(format!( return Err(Error(format!(
"length of function section is {}, while len of code section is {}", "length of function section is {}, while len of code section is {}",
function_section_len, function_section_len, code_section_len
code_section_len
))); )));
} }
// validate every function body in user modules // validate every function body in user modules
if function_section_len != 0 { if function_section_len != 0 {
// tests use invalid code // tests use invalid code
let function_section = module.function_section().expect( let function_section = module
"function_section_len != 0; qed", .function_section()
); .expect("function_section_len != 0; qed");
let code_section = module.code_section().expect( let code_section = module
"function_section_len != 0; function_section_len == code_section_len; qed", .code_section()
); .expect("function_section_len != 0; function_section_len == code_section_len; qed");
// check every function body // check every function body
for (index, function) in function_section.entries().iter().enumerate() { for (index, function) in function_section.entries().iter().enumerate() {
let function_body = code_section.bodies().get(index as usize).ok_or( let function_body = code_section
Error(format!( .bodies()
"Missing body for function {}", .get(index as usize)
index .ok_or(Error(format!("Missing body for function {}", index)))?;
)), let code =
)?; FunctionReader::read_function(&context, function, function_body).map_err(|e| {
let code = FunctionReader::read_function(&context, function, function_body)
.map_err(|e| {
let Error(ref msg) = 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); 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. // HashSet::insert returns false if item already in set.
let duplicate = export_names.insert(export.field()) == false; let duplicate = export_names.insert(export.field()) == false;
if duplicate { if duplicate {
return Err(Error( return Err(Error(format!("duplicate export {}", export.field())));
format!("duplicate export {}", export.field()),
));
} }
match *export.internal() { match *export.internal() {
Internal::Function(function_index) => { Internal::Function(function_index) => {
@ -382,10 +381,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
} }
} }
Ok(ValidatedModule { Ok(ValidatedModule { module, code_map })
module,
code_map,
})
} }
fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> { 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::I64Const(_) => ValueType::I64,
Instruction::F32Const(_) => ValueType::F32, Instruction::F32Const(_) => ValueType::F32,
Instruction::F64Const(_) => ValueType::F64, Instruction::F64Const(_) => ValueType::F64,
Instruction::GetGlobal(idx) => { Instruction::GetGlobal(idx) => match globals.get(idx as usize) {
match globals.get(idx as usize) {
Some(target_global) => { Some(target_global) => {
if target_global.is_mutable() { if target_global.is_mutable() {
return Err(Error(format!("Global {} is mutable", idx))); return Err(Error(format!("Global {} is mutable", idx)));
@ -446,12 +441,12 @@ fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<Value
target_global.content_type() target_global.content_type()
} }
None => { None => {
return Err(Error( return Err(Error(format!(
format!("Global {} doesn't exists or not yet defined", idx), "Global {} doesn't exists or not yet defined",
)) idx
} )));
}
} }
},
_ => return Err(Error("Non constant opcode in init expr".into())), _ => return Err(Error("Non constant opcode in init expr".into())),
}; };
if code[1] != Instruction::End { if code[1] != Instruction::End {

View File

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

View File

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

View File

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

View File

@ -1,14 +1,16 @@
#![cfg(test)] #![cfg(test)]
use std::fs::File;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File;
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value}; use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
use wasmi::memory_units::Pages; use wasmi::memory_units::Pages;
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, use wasmi::{
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap}; ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature,
TableDescriptor, TableInstance, TableRef, Trap,
};
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue { fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
match val { match val {
@ -190,7 +192,8 @@ impl SpecDriver {
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> { fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
match name { match name {
Some(name) => self.module(name), Some(name) => self.module(name),
None => self.last_module None => self
.last_module
.clone() .clone()
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())), .ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
} }
@ -301,7 +304,8 @@ fn run_action(
"Expected program to have loaded module {:?}", "Expected program to have loaded module {:?}",
module module
)); ));
let vec_args = args.iter() let vec_args = args
.iter()
.cloned() .cloned()
.map(spec_to_runtime_value) .map(spec_to_runtime_value)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -345,9 +349,12 @@ fn try_spec(name: &str) -> Result<(), Error> {
use std::io::Read; use std::io::Read;
let mut spec_source = Vec::new(); let mut spec_source = Vec::new();
let mut spec_file = File::open(&spec_script_path).expect("Can't open file"); let mut spec_file = File::open(&spec_script_path).expect("Can't open file");
spec_file.read_to_end(&mut spec_source).expect("Can't read file"); spec_file
.read_to_end(&mut spec_source)
.expect("Can't read file");
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)).expect("Can't read spec script"); let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name))
.expect("Can't read spec script");
let mut errors = vec![]; let mut errors = vec![];
while let Some(Command { kind, line }) = parser.next()? { while let Some(Command { kind, line }) = parser.next()? {
@ -414,12 +421,16 @@ fn try_spec(name: &str) -> Result<(), Error> {
Ok(result) => { Ok(result) => {
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() { for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
match actual_result { match actual_result {
RuntimeValue::F32(val) => if !val.is_nan() { RuntimeValue::F32(val) => {
if !val.is_nan() {
panic!("Expected nan value, got {:?}", val) 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) panic!("Expected nan value, got {:?}", val)
}, }
}
val @ _ => { val @ _ => {
panic!("Expected action to return float value, got {:?}", 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); let result = run_action(&mut spec_driver, &action);
match result { match result {
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result), Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
Err(_e) => {}, Err(_e) => {}
} }
} }
CommandKind::AssertTrap { action, .. } => { 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); let module_load = try_load(&module.into_vec(), &mut spec_driver);
match module_load { match module_load {
Ok(_) => panic!("Expected invalid module definition, got some module!"), Ok(_) => panic!("Expected invalid module definition, got some module!"),
Err(_e) => {}, Err(_e) => {}
} }
} }
CommandKind::AssertUninstantiable { module, .. } => { CommandKind::AssertUninstantiable { module, .. } => {
match try_load(&module.into_vec(), &mut spec_driver) { match try_load(&module.into_vec(), &mut spec_driver) {
Ok(_) => panic!("Expected error running start function at line {}", line), Ok(_) => panic!("Expected error running start function at line {}", line),
Err(_e) => {}, Err(_e) => {}
} }
} }
CommandKind::Register { name, as_name, .. } => { CommandKind::Register { name, as_name, .. } => {

View File

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