run 'cargo fmt'

This commit is contained in:
Andrew Dirksen 2018-11-13 16:35:35 -08:00
parent 13f08b3f9e
commit 55ab18f355
29 changed files with 1570 additions and 1320 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::*;
@ -15,30 +15,33 @@ fn load_from_file(filename: &str) -> Module {
} }
fn main() { fn main() {
let args: Vec<_> = args().collect(); let args: Vec<_> = args().collect();
if args.len() != 3 { if args.len() != 3 {
println!("Usage: {} <wasm file> <arg>", args[0]); println!("Usage: {} <wasm file> <arg>", args[0]);
println!(" wasm file should contain exported `_call` function with single I32 argument"); println!(" wasm file should contain exported `_call` function with single I32 argument");
return; return;
} }
// Here we load module using dedicated for this purpose // Here we load module using dedicated for this purpose
// `load_from_file` function (which works only with modules) // `load_from_file` function (which works only with modules)
let module = load_from_file(&args[1]); let module = load_from_file(&args[1]);
// Intialize deserialized module. It adds module into It expects 3 parameters: // Intialize deserialized module. It adds module into It expects 3 parameters:
// - a name for the module // - a name for the module
// - a module declaration // - a module declaration
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here // - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197 // This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
let main = ModuleInstance::new(&module, &ImportsBuilder::default()) let main = ModuleInstance::new(&module, &ImportsBuilder::default())
.expect("Failed to instantiate module") .expect("Failed to instantiate module")
.run_start(&mut NopExternals) .run_start(&mut NopExternals)
.expect("Failed to run start function in module"); .expect("Failed to run start function in module");
// The argument should be parsable as a valid integer // The argument should be parsable as a valid integer
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,83 +3,116 @@ 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();
if args.len() < 3 { if args.len() < 3 {
println!("Usage: {} <wasm file> <exported func> [<arg>...]", args[0]); println!("Usage: {} <wasm file> <exported func> [<arg>...]", args[0]);
return; return;
} }
let func_name = &args[2]; let func_name = &args[2];
let (_, program_args) = args.split_at(3); let (_, program_args) = args.split_at(3);
let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized"); let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized");
// Extracts call arguments from command-line arguments // Extracts call arguments from command-line arguments
let args = { let args = {
// 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
// Type section stores function types which are referenced by function_section entries .function_section()
let type_section = module.type_section().expect("No type section found"); .expect("No function section found");
// Type section stores function types which are referenced by function_section entries
let type_section = module.type_section().expect("No type section found");
// Given function name used to find export section entry which contains // 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() {
&Internal::Function(index) => index as usize, &Internal::Function(index) => index as usize,
_ => panic!("Founded export is not a function"), _ => panic!("Founded export is not a function"),
}; };
// 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()
&External::Function(_) => true, .iter()
_ => false, .filter(|entry| match entry.external() {
}).count(), &External::Function(_) => true,
None => 0, _ => false,
}; }).count(),
None => 0,
};
// Calculates a function index within module's function section // Calculates a function index within module's function section
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] {
&Type::Function(ref func_type) => func_type, &Type::Function(ref func_type) => func_type,
}; };
// 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");
// Intialize deserialized module. It adds module into It expects 3 parameters: // Intialize deserialized module. It adds module into It expects 3 parameters:
// - a name for the module // - a name for the module
// - a module declaration // - a module declaration
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here // - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197 // This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
let main = ModuleInstance::new(&loaded_module, &ImportsBuilder::default()) let main = ModuleInstance::new(&loaded_module, &ImportsBuilder::default())
.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");
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::*;
@ -76,6 +76,6 @@ fn main() {
.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")
} }
@ -225,8 +234,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() {
@ -242,6 +251,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

@ -101,15 +101,10 @@ pub struct Target {
pub enum Reloc { pub enum Reloc {
/// Patch the destination of the branch instruction (br, br_eqz, br_nez) /// Patch the destination of the branch instruction (br, br_eqz, br_nez)
/// at the specified pc. /// at the specified pc.
Br { Br { pc: u32 },
pc: u32,
},
/// Patch the specified destination index inside of br_table instruction at /// Patch the specified destination index inside of br_table instruction at
/// the specified pc. /// the specified pc.
BrTable { BrTable { pc: u32, idx: usize },
pc: u32,
idx: usize,
},
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -347,12 +342,12 @@ impl Instructions {
Reloc::BrTable { pc, idx } => match self.vec[pc as usize] { Reloc::BrTable { pc, idx } => match self.vec[pc as usize] {
Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc, Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc,
_ => panic!("brtable relocation points to not brtable instruction"), _ => panic!("brtable relocation points to not brtable instruction"),
} },
} }
} }
pub fn iterate_from(&self, position: u32) -> InstructionIter { pub fn iterate_from(&self, position: u32) -> InstructionIter {
InstructionIter{ InstructionIter {
instructions: &self.vec, instructions: &self.vec,
position, position,
} }
@ -376,9 +371,11 @@ impl<'a> Iterator for InstructionIter<'a> {
#[inline] #[inline]
fn next(&mut self) -> Option<<Self as Iterator>::Item> { fn next(&mut self) -> Option<<Self as Iterator>::Item> {
self.instructions.get(self.position as usize).map(|instruction| { self.instructions
self.position += 1; .get(self.position as usize)
instruction .map(|instruction| {
}) self.position += 1;
instruction
})
} }
} }

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::{RuntimeValue, FromRuntimeValue};
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::{FromRuntimeValue, 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 {
buffer[read_region.range()].as_ptr(), ::core::ptr::copy(
buffer[write_region.range()].as_mut_ptr(), buffer[read_region.range()].as_ptr(),
len, buffer[write_region.range()].as_mut_ptr(),
)} 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 {
buffer[read_region.range()].as_ptr(), ::core::ptr::copy_nonoverlapping(
buffer[write_region.range()].as_mut_ptr(), buffer[read_region.range()].as_ptr(),
len, buffer[write_region.range()].as_mut_ptr(),
)} 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"),
@ -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]
@ -803,9 +804,11 @@ 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
),)]
.iter(),
).is_ok() ).is_ok()
); );
@ -816,7 +819,8 @@ mod tests {
[ [
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(), ]
.iter(),
).is_err() ).is_err()
); );
@ -827,12 +831,11 @@ 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(&[][..], Some(ValueType::I32)),
Signature::new(&[][..], Some(ValueType::I32)), 0
0 ),)]
),) .iter(),
].iter(),
).is_err() ).is_err()
); );
} }

View File

@ -3,210 +3,213 @@
#[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) => {
impl<T: Into<$for>> $op<T> for $for { impl<T: Into<$for>> $op<T> for $for {
type Output = Self; type Output = Self;
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,
($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => { $rep,
#[derive(Copy, Clone)] $is,
pub struct $for($rep); 1 << (::core::mem::size_of::<$is>() * 8 - 1)
);
};
($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => {
#[derive(Copy, Clone)]
pub struct $for($rep);
impl_binop!($for, $is, Add, add); impl_binop!($for, $is, Add, add);
impl_binop!($for, $is, Sub, sub); impl_binop!($for, $is, Sub, sub);
impl_binop!($for, $is, Mul, mul); impl_binop!($for, $is, Mul, mul);
impl_binop!($for, $is, Div, div); impl_binop!($for, $is, Div, div);
impl_binop!($for, $is, Rem, rem); impl_binop!($for, $is, Rem, rem);
impl $for { impl $for {
pub fn from_bits(other: $rep) -> Self { pub fn from_bits(other: $rep) -> Self {
$for(other) $for(other)
} }
pub fn to_bits(self) -> $rep { pub fn to_bits(self) -> $rep {
self.0 self.0
} }
pub fn from_float(fl: $is) -> Self { pub fn from_float(fl: $is) -> Self {
fl.into() fl.into()
} }
pub fn to_float(self) -> $is { pub fn to_float(self) -> $is {
self.into() self.into()
} }
pub fn is_nan(self) -> bool { pub fn is_nan(self) -> bool {
self.to_float().is_nan() self.to_float().is_nan()
} }
pub fn abs(self) -> Self { pub fn abs(self) -> Self {
$for(self.0 & !$sign_bit) $for(self.0 & !$sign_bit)
} }
pub fn fract(self) -> Self { pub fn fract(self) -> Self {
self.to_float().fract().into() self.to_float().fract().into()
} }
pub fn min(self, other: Self) -> Self { pub fn min(self, other: Self) -> Self {
Self::from(self.to_float().min(other.to_float())) Self::from(self.to_float().min(other.to_float()))
} }
pub fn max(self, other: Self) -> Self { pub fn max(self, other: Self) -> Self {
Self::from(self.to_float().max(other.to_float())) Self::from(self.to_float().max(other.to_float()))
} }
} }
impl From<$is> for $for { impl From<$is> for $for {
fn from(other: $is) -> $for { fn from(other: $is) -> $for {
$for(other.to_bits()) $for(other.to_bits())
} }
} }
impl From<$for> for $is { impl From<$for> for $is {
fn from(other: $for) -> $is { fn from(other: $for) -> $is {
<$is>::from_bits(other.0) <$is>::from_bits(other.0)
} }
} }
impl Neg for $for { impl Neg for $for {
type Output = Self; type Output = Self;
fn neg(self) -> Self { fn neg(self) -> Self {
$for(self.0 ^ $sign_bit) $for(self.0 ^ $sign_bit)
} }
} }
impl<T: Into<$for> + Copy> PartialEq<T> for $for { impl<T: Into<$for> + Copy> PartialEq<T> for $for {
fn eq(&self, other: &T) -> bool { fn eq(&self, other: &T) -> bool {
$is::from(*self) == $is::from((*other).into()) $is::from(*self) == $is::from((*other).into())
} }
} }
impl<T: Into<$for> + Copy> PartialOrd<T> for $for { impl<T: Into<$for> + Copy> PartialOrd<T> for $for {
fn partial_cmp(&self, other: &T) -> Option<Ordering> { fn partial_cmp(&self, other: &T) -> Option<Ordering> {
$is::from(*self).partial_cmp(&$is::from((*other).into())) $is::from(*self).partial_cmp(&$is::from((*other).into()))
} }
} }
impl ::core::fmt::Debug for $for { impl ::core::fmt::Debug for $for {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
$is::from(*self).fmt(f) $is::from(*self).fmt(f)
} }
} }
} };
} }
float!(F32, u32, f32); float!(F32, u32, f32);
float!(F64, u64, f64); float!(F64, u64, f64);
impl From<u32> for F32 { impl From<u32> for F32 {
fn from(other: u32) -> Self { fn from(other: u32) -> Self {
Self::from_bits(other) Self::from_bits(other)
} }
} }
impl From<F32> for u32 { impl From<F32> for u32 {
fn from(other: F32) -> Self { fn from(other: F32) -> Self {
other.to_bits() other.to_bits()
} }
} }
impl From<u64> for F64 { impl From<u64> for F64 {
fn from(other: u64) -> Self { fn from(other: u64) -> Self {
Self::from_bits(other) Self::from_bits(other)
} }
} }
impl From<F64> for u64 { impl From<F64> for u64 {
fn from(other: F64) -> Self { fn from(other: F64) -> Self {
other.to_bits() other.to_bits()
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate rand; extern crate rand;
use self::rand::Rng; use self::rand::Rng;
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
T: Add<Output = T> T: Add<Output = T>
+ Div<Output = T> + Div<Output = T>
+ Mul<Output = T> + Mul<Output = T>
+ Sub<Output = T> + Sub<Output = T>
+ Neg<Output = T> + Neg<Output = T>
+ Copy + Copy
+ Debug + Debug
+ PartialEq, + PartialEq,
F: Into<T> F: Into<T>
+ Add<Output = F> + Add<Output = F>
+ Div<Output = F> + Div<Output = F>
+ Mul<Output = F> + Mul<Output = F>
+ Sub<Output = F> + Sub<Output = F>
+ Neg<Output = F> + Neg<Output = F>
+ Copy + Copy
+ Debug, + Debug,
I: IntoIterator<Item = (F, F)>, I: IntoIterator<Item = (F, F)>,
{ {
for (a, b) in iter { for (a, b) in iter {
assert_eq!((a + b).into(), a.into() + b.into()); assert_eq!((a + b).into(), a.into() + b.into());
assert_eq!((a - b).into(), a.into() - b.into()); assert_eq!((a - b).into(), a.into() - b.into());
assert_eq!((a * b).into(), a.into() * b.into()); assert_eq!((a * b).into(), a.into() * b.into());
assert_eq!((a / b).into(), a.into() / b.into()); assert_eq!((a / b).into(), a.into() / b.into());
assert_eq!((-a).into(), -a.into()); assert_eq!((-a).into(), -a.into());
assert_eq!((-b).into(), -b.into()); assert_eq!((-b).into(), -b.into());
} }
} }
#[test] #[test]
fn test_ops_f32() { fn test_ops_f32() {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let iter = iter::repeat(()).map(|_| rng.gen()); let iter = iter::repeat(()).map(|_| rng.gen());
test_ops::<F32, f32, _>(iter.take(1000)); test_ops::<F32, f32, _>(iter.take(1000));
} }
#[test] #[test]
fn test_ops_f64() { fn test_ops_f64() {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let iter = iter::repeat(()).map(|_| rng.gen()); let iter = iter::repeat(()).map(|_| rng.gen());
test_ops::<F64, f64, _>(iter.take(1000)); test_ops::<F64, f64, _>(iter.take(1000));
} }
#[test] #[test]
fn test_neg_nan_f32() { fn test_neg_nan_f32() {
assert_eq!((-F32(0xff80_3210)).0, 0x7f80_3210); assert_eq!((-F32(0xff80_3210)).0, 0x7f80_3210);
} }
#[test] #[test]
fn test_neg_nan_f64() { fn test_neg_nan_f64() {
assert_eq!((-F64(0xff80_3210_0000_0000)).0, 0x7f80_3210_0000_0000); assert_eq!((-F64(0xff80_3210_0000_0000)).0, 0x7f80_3210_0000_0000);
} }
} }

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,21 +106,22 @@ 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
} else { } else {
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 +130,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 +143,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,15 +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 +607,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,11 +624,7 @@ 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" => {
@ -632,9 +633,10 @@ fn two_envs_one_externals() {
)) ))
} }
_ => { _ => {
return Err(Error::Instantiation( return Err(Error::Instantiation(format!(
format!("Export {} not found", field_name), "Export {} not found",
)) field_name
)))
} }
}; };
@ -674,7 +676,7 @@ fn two_envs_one_externals() {
&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(
&untrusted_module, &untrusted_module,
@ -682,7 +684,7 @@ fn two_envs_one_externals() {
.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
.invoke_export("test", &[], &mut HostExternals) .invoke_export("test", &[], &mut HostExternals)
@ -716,7 +718,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 +733,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 +754,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
)))
} }
} }
} }
@ -789,7 +790,7 @@ fn dynamically_add_host_func() {
&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!(
instance instance

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;
@ -109,40 +112,40 @@ fn interpreter_inc_i32() {
#[test] #[test]
fn interpreter_accumulate_u8() { fn interpreter_accumulate_u8() {
// Name of function contained in WASM file (note the leading underline) // Name of function contained in WASM file (note the leading underline)
const FUNCTION_NAME: &'static str = "_accumulate_u8"; const FUNCTION_NAME: &'static str = "_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();
// Place the octet-sequence at index 0 in linear memory // Place the octet-sequence at index 0 in linear memory
let offset: u32 = 0; let offset: u32 = 0;
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 = &[
let retval = instance RuntimeValue::I32(BUF.len() as i32),
.invoke_export(FUNCTION_NAME, args, &mut NopExternals) RuntimeValue::I32(offset as i32),
.expect("Failed to execute function"); ];
let retval = instance
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
.expect("Failed to execute function");
// For verification, repeat accumulation using native code // For verification, repeat accumulation using native code
let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32); let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32);
let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu)); let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu));
// Verify calculation from WebAssembly runtime is identical to expected result // Verify calculation from WebAssembly runtime is identical to expected result
assert_eq!(exp_retval, retval); assert_eq!(exp_retval, retval);
} }

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

@ -1,12 +1,12 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use alloc::prelude::*; use alloc::prelude::*;
use core::u32;
use parity_wasm::elements::{Instruction, BlockType, ValueType, TableElementType, Func, FuncBody};
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use core::u32;
use parity_wasm::elements::{BlockType, Func, FuncBody, Instruction, TableElementType, ValueType};
use validation::context::ModuleContext; use validation::context::ModuleContext;
use validation::Error;
use validation::util::Locals; use validation::util::Locals;
use validation::Error;
use common::stack::StackWithLimit; use common::stack::StackWithLimit;
use isa; use isa;
@ -39,13 +39,9 @@ enum BlockFrameType {
/// Usual block frame. /// Usual block frame.
/// ///
/// Can be used for an implicit function block. /// Can be used for an implicit function block.
Block { Block { end_label: LabelId },
end_label: LabelId,
},
/// Loop frame (branching to the beginning of block). /// Loop frame (branching to the beginning of block).
Loop { Loop { header: LabelId },
header: LabelId,
},
/// True-subblock of if expression. /// True-subblock of if expression.
IfTrue { IfTrue {
/// If jump happens inside the if-true block then control will /// If jump happens inside the if-true block then control will
@ -58,9 +54,7 @@ enum BlockFrameType {
if_not: LabelId, if_not: LabelId,
}, },
/// False-subblock of if expression. /// False-subblock of if expression.
IfFalse { IfFalse { end_label: LabelId },
end_label: LabelId,
}
} }
impl BlockFrameType { impl BlockFrameType {
@ -183,9 +177,7 @@ impl FunctionReader {
let end_label = context.sink.new_label(); let end_label = context.sink.new_label();
push_label( push_label(
BlockFrameType::Block { BlockFrameType::Block { end_label },
end_label,
},
result_ty, result_ty,
context.position, context.position,
&context.value_stack, &context.value_stack,
@ -198,7 +190,10 @@ impl FunctionReader {
Ok(context.into_code()) Ok(context.into_code())
} }
fn read_function_body(context: &mut FunctionValidationContext, body: &[Instruction]) -> Result<(), Error> { fn read_function_body(
context: &mut FunctionValidationContext,
body: &[Instruction],
) -> Result<(), Error> {
let body_len = body.len(); let body_len = body.len();
if body_len == 0 { if body_len == 0 {
return Err(Error("Non-empty function body expected".into())); return Err(Error("Non-empty function body expected".into()));
@ -207,15 +202,19 @@ impl FunctionReader {
loop { loop {
let instruction = &body[context.position]; let instruction = &body[context.position];
let outcome = FunctionReader::read_instruction(context, instruction) let outcome =
.map_err(|err| Error(format!("At instruction {:?}(@{}): {}", instruction, context.position, err)))?; FunctionReader::read_instruction(context, instruction).map_err(|err| {
Error(format!(
"At instruction {:?}(@{}): {}",
instruction, context.position, err
))
})?;
match outcome { match outcome {
Outcome::NextInstruction => (), Outcome::NextInstruction => (),
Outcome::Unreachable => make_top_frame_polymorphic( Outcome::Unreachable => {
&mut context.value_stack, make_top_frame_polymorphic(&mut context.value_stack, &mut context.frame_stack)
&mut context.frame_stack }
),
} }
context.position += 1; context.position += 1;
@ -225,11 +224,14 @@ impl FunctionReader {
} }
} }
fn read_instruction(context: &mut FunctionValidationContext, instruction: &Instruction) -> Result<Outcome, Error> { fn read_instruction(
context: &mut FunctionValidationContext,
instruction: &Instruction,
) -> Result<Outcome, Error> {
use self::Instruction::*; use self::Instruction::*;
match *instruction { match *instruction {
// Nop instruction doesn't do anything. It is safe to just skip it. // Nop instruction doesn't do anything. It is safe to just skip it.
Nop => {}, Nop => {}
Unreachable => { Unreachable => {
context.sink.emit(isa::Instruction::Unreachable); context.sink.emit(isa::Instruction::Unreachable);
@ -239,9 +241,7 @@ impl FunctionReader {
Block(block_type) => { Block(block_type) => {
let end_label = context.sink.new_label(); let end_label = context.sink.new_label();
push_label( push_label(
BlockFrameType::Block { BlockFrameType::Block { end_label },
end_label
},
block_type, block_type,
context.position, context.position,
&context.value_stack, &context.value_stack,
@ -254,9 +254,7 @@ impl FunctionReader {
context.sink.resolve_label(header); context.sink.resolve_label(header);
push_label( push_label(
BlockFrameType::Loop { BlockFrameType::Loop { header },
header,
},
block_type, block_type,
context.position, context.position,
&context.value_stack, &context.value_stack,
@ -269,12 +267,13 @@ impl FunctionReader {
let if_not = context.sink.new_label(); let if_not = context.sink.new_label();
let end_label = context.sink.new_label(); let end_label = context.sink.new_label();
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; pop_value(
&mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
push_label( push_label(
BlockFrameType::IfTrue { BlockFrameType::IfTrue { if_not, end_label },
if_not,
end_label,
},
block_type, block_type,
context.position, context.position,
&context.value_stack, &context.value_stack,
@ -283,14 +282,15 @@ impl FunctionReader {
context.sink.emit_br_eqz(Target { context.sink.emit_br_eqz(Target {
label: if_not, label: if_not,
drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, }, drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}); });
} }
Else => { Else => {
let (block_type, if_not, end_label) = { let (block_type, if_not, end_label) = {
let top_frame = top_label( let top_frame = top_label(&context.frame_stack);
&context.frame_stack,
);
let (if_not, end_label) = match top_frame.frame_type { let (if_not, end_label) = match top_frame.frame_type {
BlockFrameType::IfTrue { if_not, end_label } => (if_not, end_label), BlockFrameType::IfTrue { if_not, end_label } => (if_not, end_label),
@ -303,7 +303,10 @@ impl FunctionReader {
// to the "end_label" (it will be resolved at End). // to the "end_label" (it will be resolved at End).
context.sink.emit_br(Target { context.sink.emit_br(Target {
label: end_label, label: end_label,
drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, }, drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}); });
// Resolve `if_not` to here so when if condition is unsatisfied control flow // Resolve `if_not` to here so when if condition is unsatisfied control flow
@ -312,14 +315,9 @@ impl FunctionReader {
// Then, we pop the current label. It discards all values that pushed in the current // Then, we pop the current label. It discards all values that pushed in the current
// frame. // frame.
pop_label( pop_label(&mut context.value_stack, &mut context.frame_stack)?;
&mut context.value_stack,
&mut context.frame_stack
)?;
push_label( push_label(
BlockFrameType::IfFalse { BlockFrameType::IfFalse { end_label },
end_label,
},
block_type, block_type,
context.position, context.position,
&context.value_stack, &context.value_stack,
@ -335,14 +333,10 @@ impl FunctionReader {
if let BlockFrameType::IfTrue { if_not, .. } = frame_type { if let BlockFrameType::IfTrue { if_not, .. } = frame_type {
// A `if` without an `else` can't return a result. // A `if` without an `else` can't return a result.
if block_type != BlockType::NoResult { if block_type != BlockType::NoResult {
return Err( return Err(Error(format!(
Error(
format!(
"If block without else required to have NoResult block type. But it has {:?} type", "If block without else required to have NoResult block type. But it has {:?} type",
block_type block_type
) )));
)
);
} }
// Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump // Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump
@ -365,7 +359,7 @@ impl FunctionReader {
tee_value( tee_value(
&mut context.value_stack, &mut context.value_stack,
&context.frame_stack, &context.frame_stack,
value_type.into() value_type.into(),
)?; )?;
} }
@ -388,11 +382,7 @@ impl FunctionReader {
Br(depth) => { Br(depth) => {
Validator::validate_br(context, depth)?; Validator::validate_br(context, depth)?;
let target = require_target( let target = require_target(depth, &context.value_stack, &context.frame_stack);
depth,
&context.value_stack,
&context.frame_stack,
);
context.sink.emit_br(target); context.sink.emit_br(target);
return Ok(Outcome::Unreachable); return Ok(Outcome::Unreachable);
@ -400,11 +390,7 @@ impl FunctionReader {
BrIf(depth) => { BrIf(depth) => {
Validator::validate_br_if(context, depth)?; Validator::validate_br_if(context, depth)?;
let target = require_target( let target = require_target(depth, &context.value_stack, &context.frame_stack);
depth,
&context.value_stack,
&context.frame_stack,
);
context.sink.emit_br_nez(target); context.sink.emit_br_nez(target);
} }
BrTable(ref table, default) => { BrTable(ref table, default) => {
@ -412,32 +398,26 @@ impl FunctionReader {
let mut targets = Vec::new(); let mut targets = Vec::new();
for depth in table.iter() { for depth in table.iter() {
let target = require_target( let target = require_target(*depth, &context.value_stack, &context.frame_stack);
*depth,
&context.value_stack,
&context.frame_stack,
);
targets.push(target); targets.push(target);
} }
let default_target = require_target( let default_target =
default, require_target(default, &context.value_stack, &context.frame_stack);
&context.value_stack,
&context.frame_stack,
);
context.sink.emit_br_table(&targets, default_target); context.sink.emit_br_table(&targets, default_target);
return Ok(Outcome::Unreachable); return Ok(Outcome::Unreachable);
} }
Return => { Return => {
if let BlockType::Value(value_type) = context.return_type()? { if let BlockType::Value(value_type) = context.return_type()? {
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; tee_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
} }
let drop_keep = drop_keep_return( let drop_keep =
&context.locals, drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack);
&context.value_stack,
&context.frame_stack,
);
context.sink.emit(isa::Instruction::Return(drop_keep)); context.sink.emit(isa::Instruction::Return(drop_keep));
return Ok(Outcome::Unreachable); return Ok(Outcome::Unreachable);
@ -464,37 +444,19 @@ impl FunctionReader {
GetLocal(index) => { GetLocal(index) => {
// We need to calculate relative depth before validation since // We need to calculate relative depth before validation since
// it will change the value stack size. // it will change the value stack size.
let depth = relative_local_depth( let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
index,
&context.locals,
&context.value_stack,
)?;
Validator::validate_get_local(context, index)?; Validator::validate_get_local(context, index)?;
context.sink.emit( context.sink.emit(isa::Instruction::GetLocal(depth));
isa::Instruction::GetLocal(depth),
);
} }
SetLocal(index) => { SetLocal(index) => {
Validator::validate_set_local(context, index)?; Validator::validate_set_local(context, index)?;
let depth = relative_local_depth( let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
index, context.sink.emit(isa::Instruction::SetLocal(depth));
&context.locals,
&context.value_stack,
)?;
context.sink.emit(
isa::Instruction::SetLocal(depth),
);
} }
TeeLocal(index) => { TeeLocal(index) => {
Validator::validate_tee_local(context, index)?; Validator::validate_tee_local(context, index)?;
let depth = relative_local_depth( let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
index, context.sink.emit(isa::Instruction::TeeLocal(depth));
&context.locals,
&context.value_stack,
)?;
context.sink.emit(
isa::Instruction::TeeLocal(depth),
);
} }
GetGlobal(index) => { GetGlobal(index) => {
Validator::validate_get_global(context, index)?; Validator::validate_get_global(context, index)?;
@ -1136,78 +1098,160 @@ impl FunctionReader {
struct Validator; struct Validator;
impl Validator { impl Validator {
fn validate_const(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> { fn validate_const(
context: &mut FunctionValidationContext,
value_type: ValueType,
) -> Result<(), Error> {
push_value(&mut context.value_stack, value_type.into())?; push_value(&mut context.value_stack, value_type.into())?;
Ok(()) Ok(())
} }
fn validate_unop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> { fn validate_unop(
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; context: &mut FunctionValidationContext,
value_type: ValueType,
) -> Result<(), Error> {
pop_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
push_value(&mut context.value_stack, value_type.into())?; push_value(&mut context.value_stack, value_type.into())?;
Ok(()) Ok(())
} }
fn validate_binop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> { fn validate_binop(
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; context: &mut FunctionValidationContext,
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; value_type: ValueType,
) -> Result<(), Error> {
pop_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
pop_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
push_value(&mut context.value_stack, value_type.into())?; push_value(&mut context.value_stack, value_type.into())?;
Ok(()) Ok(())
} }
fn validate_testop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> { fn validate_testop(
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; context: &mut FunctionValidationContext,
value_type: ValueType,
) -> Result<(), Error> {
pop_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
push_value(&mut context.value_stack, ValueType::I32.into())?; push_value(&mut context.value_stack, ValueType::I32.into())?;
Ok(()) Ok(())
} }
fn validate_relop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> { fn validate_relop(
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; context: &mut FunctionValidationContext,
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; value_type: ValueType,
) -> Result<(), Error> {
pop_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
pop_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
push_value(&mut context.value_stack, ValueType::I32.into())?; push_value(&mut context.value_stack, ValueType::I32.into())?;
Ok(()) Ok(())
} }
fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: ValueType, value_type2: ValueType) -> Result<(), Error> { fn validate_cvtop(
pop_value(&mut context.value_stack, &context.frame_stack, value_type1.into())?; context: &mut FunctionValidationContext,
value_type1: ValueType,
value_type2: ValueType,
) -> Result<(), Error> {
pop_value(
&mut context.value_stack,
&context.frame_stack,
value_type1.into(),
)?;
push_value(&mut context.value_stack, value_type2.into())?; push_value(&mut context.value_stack, value_type2.into())?;
Ok(()) Ok(())
} }
fn validate_drop(context: &mut FunctionValidationContext) -> Result<(), Error> { fn validate_drop(context: &mut FunctionValidationContext) -> Result<(), Error> {
pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?; pop_value(
&mut context.value_stack,
&context.frame_stack,
StackValueType::Any,
)?;
Ok(()) Ok(())
} }
fn validate_select(context: &mut FunctionValidationContext) -> Result<(), Error> { fn validate_select(context: &mut FunctionValidationContext) -> Result<(), Error> {
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; pop_value(
let select_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?; &mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
let select_type = pop_value(
&mut context.value_stack,
&context.frame_stack,
StackValueType::Any,
)?;
pop_value(&mut context.value_stack, &context.frame_stack, select_type)?; pop_value(&mut context.value_stack, &context.frame_stack, select_type)?;
push_value(&mut context.value_stack, select_type)?; push_value(&mut context.value_stack, select_type)?;
Ok(()) Ok(())
} }
fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> { fn validate_get_local(
context: &mut FunctionValidationContext,
index: u32,
) -> Result<(), Error> {
let local_type = require_local(&context.locals, index)?; let local_type = require_local(&context.locals, index)?;
push_value(&mut context.value_stack, local_type.into())?; push_value(&mut context.value_stack, local_type.into())?;
Ok(()) Ok(())
} }
fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> { fn validate_set_local(
context: &mut FunctionValidationContext,
index: u32,
) -> Result<(), Error> {
let local_type = require_local(&context.locals, index)?; let local_type = require_local(&context.locals, index)?;
let value_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?; let value_type = pop_value(
if StackValueType::from(local_type) != value_type { &mut context.value_stack,
return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type))); &context.frame_stack,
StackValueType::Any,
)?;
if StackValueType::from(local_type) != value_type {
return Err(Error(format!(
"Trying to update local {} of type {:?} with value of type {:?}",
index, local_type, value_type
)));
} }
Ok(()) Ok(())
} }
fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> { fn validate_tee_local(
context: &mut FunctionValidationContext,
index: u32,
) -> Result<(), Error> {
let local_type = require_local(&context.locals, index)?; let local_type = require_local(&context.locals, index)?;
tee_value(&mut context.value_stack, &context.frame_stack, local_type.into())?; tee_value(
&mut context.value_stack,
&context.frame_stack,
local_type.into(),
)?;
Ok(()) Ok(())
} }
fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> { fn validate_get_global(
context: &mut FunctionValidationContext,
index: u32,
) -> Result<(), Error> {
let global_type: StackValueType = { let global_type: StackValueType = {
let global = context.module.require_global(index, None)?; let global = context.module.require_global(index, None)?;
global.content_type().into() global.content_type().into()
@ -1216,37 +1260,75 @@ impl Validator {
Ok(()) Ok(())
} }
fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> { fn validate_set_global(
context: &mut FunctionValidationContext,
index: u32,
) -> Result<(), Error> {
let global_type: StackValueType = { let global_type: StackValueType = {
let global = context.module.require_global(index, Some(true))?; let global = context.module.require_global(index, Some(true))?;
global.content_type().into() global.content_type().into()
}; };
let value_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?; let value_type = pop_value(
&mut context.value_stack,
&context.frame_stack,
StackValueType::Any,
)?;
if global_type != value_type { if global_type != value_type {
return Err(Error(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type))); return Err(Error(format!(
"Trying to update global {} of type {:?} with value of type {:?}",
index, global_type, value_type
)));
} }
Ok(()) Ok(())
} }
fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result<(), Error> { fn validate_load(
context: &mut FunctionValidationContext,
align: u32,
max_align: u32,
value_type: ValueType,
) -> Result<(), Error> {
if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align {
return Err(Error(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align))); return Err(Error(format!(
"Too large memory alignment 2^{} (expected at most {})",
align, max_align
)));
} }
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; pop_value(
&mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
context.module.require_memory(DEFAULT_MEMORY_INDEX)?; context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
push_value(&mut context.value_stack, value_type.into())?; push_value(&mut context.value_stack, value_type.into())?;
Ok(()) Ok(())
} }
fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result<(), Error> { fn validate_store(
context: &mut FunctionValidationContext,
align: u32,
max_align: u32,
value_type: ValueType,
) -> Result<(), Error> {
if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align {
return Err(Error(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align))); return Err(Error(format!(
"Too large memory alignment 2^{} (expected at most {})",
align, max_align
)));
} }
context.module.require_memory(DEFAULT_MEMORY_INDEX)?; context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; pop_value(
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; &mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
pop_value(
&mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
Ok(()) Ok(())
} }
@ -1257,14 +1339,22 @@ impl Validator {
}; };
if !frame_type.is_loop() { if !frame_type.is_loop() {
if let BlockType::Value(value_type) = frame_block_type { if let BlockType::Value(value_type) = frame_block_type {
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; tee_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
} }
} }
Ok(()) Ok(())
} }
fn validate_br_if(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> { fn validate_br_if(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> {
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; pop_value(
&mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
let (frame_type, frame_block_type) = { let (frame_type, frame_block_type) = {
let frame = require_label(depth, &context.frame_stack)?; let frame = require_label(depth, &context.frame_stack)?;
@ -1272,13 +1362,21 @@ impl Validator {
}; };
if !frame_type.is_loop() { if !frame_type.is_loop() {
if let BlockType::Value(value_type) = frame_block_type { if let BlockType::Value(value_type) = frame_block_type {
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; tee_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
} }
} }
Ok(()) Ok(())
} }
fn validate_br_table(context: &mut FunctionValidationContext, table: &[u32], default: u32) -> Result<(), Error> { fn validate_br_table(
context: &mut FunctionValidationContext,
table: &[u32],
default: u32,
) -> Result<(), Error> {
let required_block_type: BlockType = { let required_block_type: BlockType = {
let default_block = require_label(default, &context.frame_stack)?; let default_block = require_label(default, &context.frame_stack)?;
let required_block_type = if !default_block.frame_type.is_loop() { let required_block_type = if !default_block.frame_type.is_loop() {
@ -1295,23 +1393,26 @@ impl Validator {
BlockType::NoResult BlockType::NoResult
}; };
if required_block_type != label_block_type { if required_block_type != label_block_type {
return Err( return Err(Error(format!(
Error( "Labels in br_table points to block of different types: {:?} and {:?}",
format!( required_block_type, label_block.block_type
"Labels in br_table points to block of different types: {:?} and {:?}", )));
required_block_type,
label_block.block_type
)
)
);
} }
} }
required_block_type required_block_type
}; };
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; pop_value(
&mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
if let BlockType::Value(value_type) = required_block_type { if let BlockType::Value(value_type) = required_block_type {
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; tee_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
} }
Ok(()) Ok(())
@ -1320,7 +1421,11 @@ impl Validator {
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> { fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> {
let (argument_types, return_type) = context.module.require_function(idx)?; let (argument_types, return_type) = context.module.require_function(idx)?;
for argument_type in argument_types.iter().rev() { for argument_type in argument_types.iter().rev() {
pop_value(&mut context.value_stack, &context.frame_stack, (*argument_type).into())?; pop_value(
&mut context.value_stack,
&context.frame_stack,
(*argument_type).into(),
)?;
} }
if let BlockType::Value(value_type) = return_type { if let BlockType::Value(value_type) = return_type {
push_value(&mut context.value_stack, value_type.into())?; push_value(&mut context.value_stack, value_type.into())?;
@ -1328,7 +1433,10 @@ impl Validator {
Ok(()) Ok(())
} }
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> { fn validate_call_indirect(
context: &mut FunctionValidationContext,
idx: u32,
) -> Result<(), Error> {
{ {
let table = context.module.require_table(DEFAULT_TABLE_INDEX)?; let table = context.module.require_table(DEFAULT_TABLE_INDEX)?;
if table.elem_type() != TableElementType::AnyFunc { if table.elem_type() != TableElementType::AnyFunc {
@ -1340,10 +1448,18 @@ impl Validator {
} }
} }
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; pop_value(
&mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
let (argument_types, return_type) = context.module.require_function_type(idx)?; let (argument_types, return_type) = context.module.require_function_type(idx)?;
for argument_type in argument_types.iter().rev() { for argument_type in argument_types.iter().rev() {
pop_value(&mut context.value_stack, &context.frame_stack, (*argument_type).into())?; pop_value(
&mut context.value_stack,
&context.frame_stack,
(*argument_type).into(),
)?;
} }
if let BlockType::Value(value_type) = return_type { if let BlockType::Value(value_type) = return_type {
push_value(&mut context.value_stack, value_type.into())?; push_value(&mut context.value_stack, value_type.into())?;
@ -1359,7 +1475,11 @@ impl Validator {
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<(), Error> { fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<(), Error> {
context.module.require_memory(DEFAULT_MEMORY_INDEX)?; context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; pop_value(
&mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
push_value(&mut context.value_stack, ValueType::I32.into())?; push_value(&mut context.value_stack, ValueType::I32.into())?;
Ok(()) Ok(())
} }
@ -1416,7 +1536,9 @@ fn make_top_frame_polymorphic(
value_stack: &mut StackWithLimit<StackValueType>, value_stack: &mut StackWithLimit<StackValueType>,
frame_stack: &mut StackWithLimit<BlockFrame>, frame_stack: &mut StackWithLimit<BlockFrame>,
) { ) {
let frame = frame_stack.top_mut().expect("make_top_frame_polymorphic is called with empty frame stack"); let frame = frame_stack
.top_mut()
.expect("make_top_frame_polymorphic is called with empty frame stack");
value_stack.resize(frame.value_stack_len, StackValueType::Any); value_stack.resize(frame.value_stack_len, StackValueType::Any);
frame.polymorphic_stack = true; frame.polymorphic_stack = true;
} }
@ -1501,7 +1623,11 @@ fn pop_label(
match block_type { match block_type {
BlockType::NoResult => (), BlockType::NoResult => (),
BlockType::Value(required_value_type) => { BlockType::Value(required_value_type) => {
let _ = pop_value(value_stack, frame_stack, StackValueType::Specific(required_value_type))?; let _ = pop_value(
value_stack,
frame_stack,
StackValueType::Specific(required_value_type),
)?;
} }
} }
@ -1518,7 +1644,8 @@ fn pop_label(
} }
fn top_label(frame_stack: &StackWithLimit<BlockFrame>) -> &BlockFrame { fn top_label(frame_stack: &StackWithLimit<BlockFrame>) -> &BlockFrame {
frame_stack.top() frame_stack
.top()
.expect("this function can't be called with empty frame stack") .expect("this function can't be called with empty frame stack")
} }
@ -1534,8 +1661,7 @@ fn require_target(
value_stack: &StackWithLimit<StackValueType>, value_stack: &StackWithLimit<StackValueType>,
frame_stack: &StackWithLimit<BlockFrame>, frame_stack: &StackWithLimit<BlockFrame>,
) -> Target { ) -> Target {
let is_stack_polymorphic = top_label(frame_stack) let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack;
.polymorphic_stack;
let frame = let frame =
require_label(depth, frame_stack).expect("require_target called with a bogus depth"); require_label(depth, frame_stack).expect("require_target called with a bogus depth");
@ -1604,7 +1730,7 @@ fn require_local(locals: &Locals, idx: u32) -> Result<ValueType, Error> {
fn relative_local_depth( fn relative_local_depth(
idx: u32, idx: u32,
locals: &Locals, locals: &Locals,
value_stack: &StackWithLimit<StackValueType> value_stack: &StackWithLimit<StackValueType>,
) -> Result<u32, Error> { ) -> Result<u32, Error> {
let value_stack_height = value_stack.len() as u32; let value_stack_height = value_stack.len() as u32;
let locals_and_params_count = locals.count(); let locals_and_params_count = locals.count();
@ -1612,9 +1738,7 @@ fn relative_local_depth(
let depth = value_stack_height let depth = value_stack_height
.checked_add(locals_and_params_count) .checked_add(locals_and_params_count)
.and_then(|x| x.checked_sub(idx)) .and_then(|x| x.checked_sub(idx))
.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(depth) Ok(depth)
} }
@ -1655,12 +1779,15 @@ impl Sink {
self.ins.current_pc() self.ins.current_pc()
} }
fn pc_or_placeholder<F: FnOnce() -> isa::Reloc>(&mut self, label: LabelId, reloc_creator: F) -> u32 { fn pc_or_placeholder<F: FnOnce() -> isa::Reloc>(
&mut self,
label: LabelId,
reloc_creator: F,
) -> u32 {
match self.labels[label.0] { match self.labels[label.0] {
(Label::Resolved(dst_pc), _) => dst_pc, (Label::Resolved(dst_pc), _) => dst_pc,
(Label::NotResolved, ref mut unresolved) => { (Label::NotResolved, ref mut unresolved) => {
unresolved unresolved.push(reloc_creator());
.push(reloc_creator());
u32::max_value() u32::max_value()
} }
} }
@ -1671,10 +1798,7 @@ impl Sink {
} }
fn emit_br(&mut self, target: Target) { fn emit_br(&mut self, target: Target) {
let Target { let Target { label, drop_keep } = target;
label,
drop_keep,
} = target;
let pc = self.cur_pc(); let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
self.ins.push(isa::Instruction::Br(isa::Target { self.ins.push(isa::Instruction::Br(isa::Target {
@ -1684,10 +1808,7 @@ impl Sink {
} }
fn emit_br_eqz(&mut self, target: Target) { fn emit_br_eqz(&mut self, target: Target) {
let Target { let Target { label, drop_keep } = target;
label,
drop_keep,
} = target;
let pc = self.cur_pc(); let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
self.ins.push(isa::Instruction::BrIfEqz(isa::Target { self.ins.push(isa::Instruction::BrIfEqz(isa::Target {
@ -1697,10 +1818,7 @@ impl Sink {
} }
fn emit_br_nez(&mut self, target: Target) { fn emit_br_nez(&mut self, target: Target) {
let Target { let Target { label, drop_keep } = target;
label,
drop_keep,
} = target;
let pc = self.cur_pc(); let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
self.ins.push(isa::Instruction::BrIfNez(isa::Target { self.ins.push(isa::Instruction::BrIfNez(isa::Target {
@ -1714,26 +1832,23 @@ impl Sink {
let pc = self.cur_pc(); let pc = self.cur_pc();
let mut isa_targets = Vec::new(); let mut isa_targets = Vec::new();
for (idx, &Target { label, drop_keep }) in targets.iter().chain(iter::once(&default)).enumerate() { for (idx, &Target { label, drop_keep }) in
targets.iter().chain(iter::once(&default)).enumerate()
{
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx }); let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx });
isa_targets.push( isa_targets.push(isa::Target {
isa::Target { dst_pc,
dst_pc, drop_keep: drop_keep.into(),
drop_keep: drop_keep.into(), });
},
);
} }
self.ins.push(isa::Instruction::BrTable( self.ins
isa_targets.into_boxed_slice(), .push(isa::Instruction::BrTable(isa_targets.into_boxed_slice()));
));
} }
/// Create a new unresolved label. /// Create a new unresolved label.
fn new_label(&mut self) -> LabelId { fn new_label(&mut self) -> LabelId {
let label_idx = self.labels.len(); let label_idx = self.labels.len();
self.labels.push( self.labels.push((Label::NotResolved, Vec::new()));
(Label::NotResolved, Vec::new()),
);
LabelId(label_idx) LabelId(label_idx)
} }
@ -1762,14 +1877,18 @@ impl Sink {
/// Consume this Sink and returns isa::Instructions. /// Consume this Sink and returns isa::Instructions.
fn into_inner(self) -> isa::Instructions { fn into_inner(self) -> isa::Instructions {
// At this moment all labels should be resolved. // At this moment all labels should be resolved.
assert!({ assert!(
self.labels.iter().all(|(state, unresolved)| {
match (state, unresolved) { self.labels
(Label::Resolved(_), unresolved) if unresolved.is_empty() => true, .iter()
_ => false, .all(|(state, unresolved)| match (state, unresolved) {
} (Label::Resolved(_), unresolved) if unresolved.is_empty() => true,
) _ => false,
}, "there are unresolved labels left: {:?}", self.labels); })
},
"there are unresolved labels left: {:?}",
self.labels
);
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)
@ -189,8 +190,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
.map(|&Type::Function(ref ty)| ty) .map(|&Type::Function(ref ty)| ty)
.cloned() .cloned()
.collect() .collect()
}) }).unwrap_or_default(),
.unwrap_or_default(),
); );
// Fill elements with imported values. // Fill elements with imported values.
@ -245,32 +245,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 +293,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 +380,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> {
@ -413,15 +408,15 @@ fn validate_table_type(table_type: &TableType) -> Result<(), Error> {
fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) -> Result<(), Error> { fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) -> Result<(), Error> {
let init = global_entry.init_expr(); let init = global_entry.init_expr();
let init_expr_ty = expr_const_type(init, globals)?; let init_expr_ty = expr_const_type(init, globals)?;
if init_expr_ty != global_entry.global_type().content_type() { if init_expr_ty != global_entry.global_type().content_type() {
return Err(Error(format!( return Err(Error(format!(
"Trying to initialize variable of type {:?} with value of type {:?}", "Trying to initialize variable of type {:?} with value of type {:?}",
global_entry.global_type().content_type(), global_entry.global_type().content_type(),
init_expr_ty init_expr_ty
))); )));
} }
Ok(()) Ok(())
} }
/// Returns type of this constant expression. /// Returns type of this constant expression.
@ -437,21 +432,20 @@ 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)));
}
target_global.content_type()
}
None => {
return Err(Error(
format!("Global {} doesn't exists or not yet defined", idx),
))
} }
target_global.content_type()
} }
} None => {
return Err(Error(format!(
"Global {} doesn't exists or not yet defined",
idx
)))
}
},
_ => return Err(Error("Non constant opcode in init expr".into())), _ => return Err(Error("Non constant opcode in init expr".into())),
}; };
if code[1] != Instruction::End { if code[1] != Instruction::End {

View File

@ -1,11 +1,10 @@
use super::{validate_module, ValidatedModule}; use super::{validate_module, ValidatedModule};
use isa;
use parity_wasm::builder::module; use parity_wasm::builder::module;
use parity_wasm::elements::{ use parity_wasm::elements::{
External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType, deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr,
Instruction, Instructions, TableType, ValueType, BlockType, deserialize_buffer, Instruction, Instructions, MemoryType, Module, TableType, ValueType,
Module,
}; };
use isa;
use wabt; use wabt;
#[test] #[test]
@ -27,45 +26,34 @@ fn limits() {
for (min, max, is_valid) in test_cases { for (min, max, is_valid) in test_cases {
// defined table // defined table
let m = module() let m = module().table().with_min(min).with_max(max).build().build();
.table()
.with_min(min)
.with_max(max)
.build()
.build();
assert_eq!(validate_module(m).is_ok(), is_valid); assert_eq!(validate_module(m).is_ok(), is_valid);
// imported table // imported table
let m = module() let m = module()
.with_import( .with_import(ImportEntry::new(
ImportEntry::new( "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
let m = module() let m = module()
.memory() .memory()
.with_min(min) .with_min(min)
.with_max(max) .with_max(max)
.build() .build()
.build(); .build();
assert_eq!(validate_module(m).is_ok(), is_valid); assert_eq!(validate_module(m).is_ok(), is_valid);
// imported table // imported table
let m = module() let m = module()
.with_import( .with_import(ImportEntry::new(
ImportEntry::new( "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);
} }
} }
@ -73,92 +61,63 @@ fn limits() {
#[test] #[test]
fn global_init_const() { fn global_init_const() {
let m = module() let m = module()
.with_global( .with_global(GlobalEntry::new(
GlobalEntry::new( GlobalType::new(ValueType::I32, true),
GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
InitExpr::new( )).build();
vec![Instruction::I32Const(42), Instruction::End]
)
)
)
.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
let m = module() let m = module()
.with_global( .with_global(GlobalEntry::new(
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());
} }
#[test] #[test]
fn global_init_global() { fn global_init_global() {
let m = module() let m = module()
.with_import( .with_import(ImportEntry::new(
ImportEntry::new( "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(
) GlobalType::new(ValueType::I32, true),
) InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
.with_global( )).build();
GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
)
)
.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
let m = module() let m = module()
.with_global( .with_global(GlobalEntry::new(
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
let m = module() let m = module()
.with_import( .with_import(ImportEntry::new(
ImportEntry::new( "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(
) GlobalType::new(ValueType::I32, true),
) InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
.with_global( )).build();
GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
)
)
.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.
let m = module() let m = module()
.with_global( .with_global(GlobalEntry::new(
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(
) GlobalType::new(ValueType::I32, true),
) InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
.with_global( )).build();
GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
)
)
.build();
assert!(validate_module(m).is_err()); assert!(validate_module(m).is_err());
} }
@ -166,35 +125,26 @@ fn global_init_global() {
fn global_init_misc() { fn global_init_misc() {
// without delimiting End opcode // without delimiting End opcode
let m = module() let m = module()
.with_global( .with_global(GlobalEntry::new(
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
let m = module() let m = module()
.with_global( .with_global(GlobalEntry::new(
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
let m = module() let m = module()
.with_global( .with_global(GlobalEntry::new(
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());
} }
@ -202,31 +152,25 @@ fn global_init_misc() {
fn module_limits_validity() { fn module_limits_validity() {
// module cannot contain more than 1 memory atm. // module cannot contain more than 1 memory atm.
let m = module() let m = module()
.with_import( .with_import(ImportEntry::new(
ImportEntry::new( "core".into(),
"core".into(), "memory".into(),
"memory".into(), External::Memory(MemoryType::new(10, None)),
External::Memory(MemoryType::new(10, None)) )).memory()
) .with_min(10)
) .build()
.memory()
.with_min(10)
.build()
.build(); .build();
assert!(validate_module(m).is_err()); assert!(validate_module(m).is_err());
// module cannot contain more than 1 table atm. // module cannot contain more than 1 table atm.
let m = module() let m = module()
.with_import( .with_import(ImportEntry::new(
ImportEntry::new( "core".into(),
"core".into(), "table".into(),
"table".into(), External::Table(TableType::new(10, None)),
External::Table(TableType::new(10, None)) )).table()
) .with_min(10)
) .build()
.table()
.with_min(10)
.build()
.build(); .build();
assert!(validate_module(m).is_err()); assert!(validate_module(m).is_err());
} }
@ -236,19 +180,27 @@ fn funcs() {
// recursive function calls is legal. // recursive function calls is legal.
let m = module() let m = module()
.function() .function()
.signature().return_type().i32().build() .signature()
.body().with_instructions(Instructions::new(vec![ .return_type()
Instruction::Call(1), .i32()
Instruction::End, .build()
])).build() .body()
.build() .with_instructions(Instructions::new(vec![
Instruction::Call(1),
Instruction::End,
])).build()
.build()
.function() .function()
.signature().return_type().i32().build() .signature()
.body().with_instructions(Instructions::new(vec![ .return_type()
Instruction::Call(0), .i32()
Instruction::End, .build()
])).build() .body()
.build() .with_instructions(Instructions::new(vec![
Instruction::Call(0),
Instruction::End,
])).build()
.build()
.build(); .build();
assert!(validate_module(m).is_ok()); assert!(validate_module(m).is_ok());
} }
@ -257,26 +209,20 @@ fn funcs() {
fn globals() { fn globals() {
// import immutable global is legal. // import immutable global is legal.
let m = module() let m = module()
.with_import( .with_import(ImportEntry::new(
ImportEntry::new( "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.
let m = module() let m = module()
.with_import( .with_import(ImportEntry::new(
ImportEntry::new( "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());
} }
@ -284,21 +230,23 @@ fn globals() {
fn if_else_with_return_type_validation() { fn if_else_with_return_type_validation() {
let m = module() let m = module()
.function() .function()
.signature().build() .signature()
.body().with_instructions(Instructions::new(vec![ .build()
Instruction::I32Const(1), .body()
Instruction::If(BlockType::NoResult), .with_instructions(Instructions::new(vec![
Instruction::I32Const(1), Instruction::I32Const(1),
Instruction::If(BlockType::Value(ValueType::I32)), Instruction::If(BlockType::NoResult),
Instruction::I32Const(1), Instruction::I32Const(1),
Instruction::Else, Instruction::If(BlockType::Value(ValueType::I32)),
Instruction::I32Const(2), Instruction::I32Const(1),
Instruction::End, Instruction::Else,
Instruction::Drop, Instruction::I32Const(2),
Instruction::End, Instruction::End,
Instruction::End, Instruction::Drop,
])).build() Instruction::End,
.build() Instruction::End,
])).build()
.build()
.build(); .build();
validate_module(m).unwrap(); validate_module(m).unwrap();
} }
@ -323,7 +271,7 @@ fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
instructions.push(instruction.clone()); instructions.push(instruction.clone());
pcs.push(pc); pcs.push(pc);
} else { } else {
break break;
} }
} }
@ -332,32 +280,34 @@ fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
#[test] #[test]
fn implicit_return_no_value() { fn implicit_return_no_value() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![isa::Instruction::Return(isa::DropKeep {
isa::Instruction::Return(isa::DropKeep { drop: 0,
drop: 0, keep: isa::Keep::None,
keep: isa::Keep::None, })]
})
]
) )
} }
#[test] #[test]
fn implicit_return_with_value() { fn implicit_return_with_value() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (result i32) (func (export "call") (result i32)
i32.const 0 i32.const 0
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -372,32 +322,34 @@ fn implicit_return_with_value() {
#[test] #[test]
fn implicit_return_param() { fn implicit_return_param() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (param i32) (func (export "call") (param i32)
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![isa::Instruction::Return(isa::DropKeep {
isa::Instruction::Return(isa::DropKeep { drop: 1,
drop: 1, keep: isa::Keep::None,
keep: isa::Keep::None, }),]
}),
]
) )
} }
#[test] #[test]
fn get_local() { fn get_local() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (param i32) (result i32) (func (export "call") (param i32) (result i32)
get_local 0 get_local 0
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -412,14 +364,16 @@ fn get_local() {
#[test] #[test]
fn explicit_return() { fn explicit_return() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (param i32) (result i32) (func (export "call") (param i32) (result i32)
get_local 0 get_local 0
return return
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -438,7 +392,8 @@ fn explicit_return() {
#[test] #[test]
fn add_params() { fn add_params() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (param i32) (param i32) (result i32) (func (export "call") (param i32) (param i32) (result i32)
get_local 0 get_local 0
@ -446,7 +401,8 @@ fn add_params() {
i32.add i32.add
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -468,7 +424,8 @@ fn add_params() {
#[test] #[test]
fn drop_locals() { fn drop_locals() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (param i32) (func (export "call") (param i32)
(local i32) (local i32)
@ -476,7 +433,8 @@ fn drop_locals() {
set_local 1 set_local 1
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -492,7 +450,8 @@ fn drop_locals() {
#[test] #[test]
fn if_without_else() { fn if_without_else() {
let (code, pcs) = compile(r#" let (code, pcs) = compile(
r#"
(module (module
(func (export "call") (param i32) (result i32) (func (export "call") (param i32) (result i32)
i32.const 1 i32.const 1
@ -503,7 +462,8 @@ fn if_without_else() {
i32.const 3 i32.const 3
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -517,7 +477,7 @@ fn if_without_else() {
}), }),
isa::Instruction::I32Const(2), isa::Instruction::I32Const(2),
isa::Instruction::Return(isa::DropKeep { isa::Instruction::Return(isa::DropKeep {
drop: 1, // 1 param drop: 1, // 1 param
keep: isa::Keep::Single, // 1 result keep: isa::Keep::Single, // 1 result
}), }),
isa::Instruction::I32Const(3), isa::Instruction::I32Const(3),
@ -531,7 +491,8 @@ fn if_without_else() {
#[test] #[test]
fn if_else() { fn if_else() {
let (code, pcs) = compile(r#" let (code, pcs) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
(local i32) (local i32)
@ -545,7 +506,8 @@ fn if_else() {
end end
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -578,7 +540,8 @@ fn if_else() {
#[test] #[test]
fn if_else_returns_result() { fn if_else_returns_result() {
let (code, pcs) = compile(r#" let (code, pcs) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
i32.const 1 i32.const 1
@ -590,7 +553,8 @@ fn if_else_returns_result() {
drop drop
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -622,7 +586,8 @@ fn if_else_returns_result() {
#[test] #[test]
fn if_else_branch_from_true_branch() { fn if_else_branch_from_true_branch() {
let (code, pcs) = compile(r#" let (code, pcs) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
i32.const 1 i32.const 1
@ -638,7 +603,8 @@ fn if_else_branch_from_true_branch() {
drop drop
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -680,7 +646,8 @@ fn if_else_branch_from_true_branch() {
#[test] #[test]
fn if_else_branch_from_false_branch() { fn if_else_branch_from_false_branch() {
let (code, pcs) = compile(r#" let (code, pcs) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
i32.const 1 i32.const 1
@ -696,7 +663,8 @@ fn if_else_branch_from_false_branch() {
drop drop
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -738,7 +706,8 @@ fn if_else_branch_from_false_branch() {
#[test] #[test]
fn loop_() { fn loop_() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
loop (result i32) loop (result i32)
@ -749,7 +718,8 @@ fn loop_() {
drop drop
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -773,28 +743,29 @@ fn loop_() {
#[test] #[test]
fn loop_empty() { fn loop_empty() {
let (code, _) = compile(r#" let (code, _) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
loop loop
end end
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![isa::Instruction::Return(isa::DropKeep {
isa::Instruction::Return(isa::DropKeep { drop: 0,
drop: 0, keep: isa::Keep::None,
keep: isa::Keep::None, }),]
}),
]
) )
} }
#[test] #[test]
fn brtable() { fn brtable() {
let (code, pcs) = compile(r#" let (code, pcs) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
block $1 block $1
@ -805,7 +776,8 @@ fn brtable() {
end end
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -838,7 +810,8 @@ fn brtable() {
#[test] #[test]
fn brtable_returns_result() { fn brtable_returns_result() {
let (code, pcs) = compile(r#" let (code, pcs) = compile(
r#"
(module (module
(func (export "call") (func (export "call")
block $1 (result i32) block $1 (result i32)
@ -852,7 +825,8 @@ fn brtable_returns_result() {
drop drop
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![
@ -888,7 +862,8 @@ fn brtable_returns_result() {
#[test] #[test]
fn wabt_example() { fn wabt_example() {
let (code, pcs) = compile(r#" let (code, pcs) = compile(
r#"
(module (module
(func (export "call") (param i32) (result i32) (func (export "call") (param i32) (result i32)
block $exit block $exit
@ -901,7 +876,8 @@ fn wabt_example() {
return return
) )
) )
"#); "#,
);
assert_eq!( assert_eq!(
code, code,
vec![ vec![

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 {
@ -21,30 +23,30 @@ fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
#[derive(Debug)] #[derive(Debug)]
enum Error { enum Error {
Load(String), Load(String),
Start(Trap), Start(Trap),
Script(script::Error), Script(script::Error),
Interpreter(InterpreterError), Interpreter(InterpreterError),
} }
impl From<InterpreterError> for Error { impl From<InterpreterError> for Error {
fn from(e: InterpreterError) -> Error { fn from(e: InterpreterError) -> Error {
Error::Interpreter(e) Error::Interpreter(e)
} }
} }
impl From<script::Error> for Error { impl From<script::Error> for Error {
fn from(e: script::Error) -> Error { fn from(e: script::Error) -> Error {
Error::Script(e) Error::Script(e)
} }
} }
struct SpecModule { struct SpecModule {
table: TableRef, table: TableRef,
memory: MemoryRef, memory: MemoryRef,
global_i32: GlobalRef, global_i32: GlobalRef,
global_f32: GlobalRef, global_f32: GlobalRef,
global_f64: GlobalRef, global_f64: GlobalRef,
} }
impl SpecModule { impl SpecModule {
@ -62,27 +64,27 @@ impl SpecModule {
const PRINT_FUNC_INDEX: usize = 0; const PRINT_FUNC_INDEX: usize = 0;
impl Externals for SpecModule { impl Externals for SpecModule {
fn invoke_index( fn invoke_index(
&mut self, &mut self,
index: usize, index: usize,
args: RuntimeArgs, args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> { ) -> Result<Option<RuntimeValue>, Trap> {
match index { match index {
PRINT_FUNC_INDEX => { PRINT_FUNC_INDEX => {
println!("print: {:?}", args); println!("print: {:?}", args);
Ok(None) Ok(None)
} }
_ => panic!("SpecModule doesn't provide function at index {}", index), _ => panic!("SpecModule doesn't provide function at index {}", index),
} }
} }
} }
impl ModuleImportResolver for SpecModule { impl ModuleImportResolver for SpecModule {
fn resolve_func( fn resolve_func(
&self, &self,
field_name: &str, field_name: &str,
func_type: &Signature, func_type: &Signature,
) -> Result<FuncRef, InterpreterError> { ) -> Result<FuncRef, InterpreterError> {
let index = match field_name { let index = match field_name {
"print" => PRINT_FUNC_INDEX, "print" => PRINT_FUNC_INDEX,
"print_i32" => PRINT_FUNC_INDEX, "print_i32" => PRINT_FUNC_INDEX,
@ -104,15 +106,15 @@ impl ModuleImportResolver for SpecModule {
)); ));
} }
let func = FuncInstance::alloc_host(func_type.clone(), index); let func = FuncInstance::alloc_host(func_type.clone(), index);
return Ok(func); return Ok(func);
} }
fn resolve_global( fn resolve_global(
&self, &self,
field_name: &str, field_name: &str,
_global_type: &GlobalDescriptor, _global_type: &GlobalDescriptor,
) -> Result<GlobalRef, InterpreterError> { ) -> Result<GlobalRef, InterpreterError> {
match field_name { match field_name {
"global_i32" => Ok(self.global_i32.clone()), "global_i32" => Ok(self.global_i32.clone()),
"global_f32" => Ok(self.global_f32.clone()), "global_f32" => Ok(self.global_f32.clone()),
@ -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<_>>();
@ -322,8 +326,7 @@ fn run_action(
.export_by_name(&field) .export_by_name(&field)
.ok_or_else(|| { .ok_or_else(|| {
InterpreterError::Global(format!("Expected to have export with name {}", field)) InterpreterError::Global(format!("Expected to have export with name {}", field))
})? })?.as_global()
.as_global()
.cloned() .cloned()
.ok_or_else(|| { .ok_or_else(|| {
InterpreterError::Global(format!("Expected export {} to be a global", field)) InterpreterError::Global(format!("Expected export {} to be a global", field))
@ -345,9 +348,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()? {
@ -435,7 +441,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 +462,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;