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

View File

@ -4,7 +4,7 @@ extern crate wasmi;
use std::env::args;
use std::fs::File;
use wasmi::{ModuleInstance, NopExternals, RuntimeValue, ImportsBuilder, Module};
use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue};
fn load_from_file(filename: &str) -> Module {
use std::io::prelude::*;
@ -15,30 +15,33 @@ fn load_from_file(filename: &str) -> Module {
}
fn main() {
let args: Vec<_> = args().collect();
if args.len() != 3 {
println!("Usage: {} <wasm file> <arg>", args[0]);
println!(" wasm file should contain exported `_call` function with single I32 argument");
return;
}
let args: Vec<_> = args().collect();
if args.len() != 3 {
println!("Usage: {} <wasm file> <arg>", args[0]);
println!(" wasm file should contain exported `_call` function with single I32 argument");
return;
}
// Here we load module using dedicated for this purpose
// `load_from_file` function (which works only with modules)
let module = load_from_file(&args[1]);
// Here we load module using dedicated for this purpose
// `load_from_file` function (which works only with modules)
let module = load_from_file(&args[1]);
// Intialize deserialized module. It adds module into It expects 3 parameters:
// - a name for the module
// - a module declaration
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
let main = ModuleInstance::new(&module, &ImportsBuilder::default())
.expect("Failed to instantiate module")
.run_start(&mut NopExternals)
.expect("Failed to run start function in module");
// Intialize deserialized module. It adds module into It expects 3 parameters:
// - a name for the module
// - a module declaration
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
let main = ModuleInstance::new(&module, &ImportsBuilder::default())
.expect("Failed to instantiate module")
.run_start(&mut NopExternals)
.expect("Failed to run start function in module");
// The argument should be parsable as a valid integer
let argument: i32 = args[2].parse().expect("Integer argument required");
// The argument should be parsable as a valid integer
let argument: i32 = args[2].parse().expect("Integer argument required");
// "_call" export of function to be executed with an i32 argument and prints the result of execution
println!("Result: {:?}", main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals));
// "_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)
);
}

View File

@ -3,83 +3,116 @@ extern crate wasmi;
use std::env::args;
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType};
use wasmi::{RuntimeValue, ModuleInstance, NopExternals, ImportsBuilder};
use parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType};
use wasmi::{ImportsBuilder, ModuleInstance, NopExternals, RuntimeValue};
fn main() {
let args: Vec<_> = args().collect();
if args.len() < 3 {
println!("Usage: {} <wasm file> <exported func> [<arg>...]", args[0]);
return;
}
let func_name = &args[2];
let (_, program_args) = args.split_at(3);
let args: Vec<_> = args().collect();
if args.len() < 3 {
println!("Usage: {} <wasm file> <exported func> [<arg>...]", args[0]);
return;
}
let func_name = &args[2];
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
let args = {
// Export section has an entry with a func_name with an index inside a module
let export_section = module.export_section().expect("No export section found");
// It's a section with function declarations (which are references to the type section entries)
let function_section = module.function_section().expect("No function section found");
// Type section stores function types which are referenced by function_section entries
let type_section = module.type_section().expect("No type section found");
// Extracts call arguments from command-line arguments
let args = {
// Export section has an entry with a func_name with an index inside a module
let export_section = module.export_section().expect("No export section found");
// It's a section with function declarations (which are references to the type section entries)
let function_section = module
.function_section()
.expect("No function section found");
// Type section stores function types which are referenced by function_section entries
let type_section = module.type_section().expect("No type section found");
// Given function name used to find export section entry which contains
// an `internal` field which points to the index in the function index space
let found_entry = export_section.entries().iter()
.find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name));
// Given function name used to find export section entry which contains
// an `internal` field which points to the index in the function index space
let found_entry = export_section
.entries()
.iter()
.find(|entry| func_name == entry.field())
.expect(&format!("No export with name {} found", func_name));
// Function index in the function index space (internally-defined + imported)
let function_index: usize = match found_entry.internal() {
&Internal::Function(index) => index as usize,
_ => panic!("Founded export is not a function"),
};
// Function index in the function index space (internally-defined + imported)
let function_index: usize = match found_entry.internal() {
&Internal::Function(index) => index as usize,
_ => panic!("Founded export is not a function"),
};
// We need to count import section entries (functions only!) to subtract it from function_index
// and obtain the index within the function section
let import_section_len: usize = match module.import_section() {
Some(import) =>
import.entries().iter().filter(|entry| match entry.external() {
&External::Function(_) => true,
_ => false,
}).count(),
None => 0,
};
// We need to count import section entries (functions only!) to subtract it from function_index
// and obtain the index within the function section
let import_section_len: usize = match module.import_section() {
Some(import) => import
.entries()
.iter()
.filter(|entry| match entry.external() {
&External::Function(_) => true,
_ => false,
}).count(),
None => 0,
};
// Calculates a function index within module's function section
let function_index_in_section = function_index - import_section_len;
// Calculates a function index within module's function section
let function_index_in_section = function_index - import_section_len;
// Getting a type reference from a function section entry
let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize;
// 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;
// Use the reference to get an actual function type
let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
&Type::Function(ref func_type) => func_type,
};
// Use the reference to get an actual function type
let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
&Type::Function(ref func_type) => func_type,
};
// Parses arguments and constructs runtime values in correspondence of their types
function_type.params().iter().enumerate().map(|(i, value)| match value {
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i])).into()),
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i])).into()),
}).collect::<Vec<RuntimeValue>>()
};
// Parses arguments and constructs runtime values in correspondence of their types
function_type
.params()
.iter()
.enumerate()
.map(|(i, value)| match value {
&ValueType::I32 => RuntimeValue::I32(
program_args[i]
.parse::<i32>()
.expect(&format!("Can't parse arg #{} as i32", program_args[i])),
),
&ValueType::I64 => RuntimeValue::I64(
program_args[i]
.parse::<i64>()
.expect(&format!("Can't parse arg #{} as i64", program_args[i])),
),
&ValueType::F32 => RuntimeValue::F32(
program_args[i]
.parse::<f32>()
.expect(&format!("Can't parse arg #{} as f32", program_args[i]))
.into(),
),
&ValueType::F64 => RuntimeValue::F64(
program_args[i]
.parse::<f64>()
.expect(&format!("Can't parse arg #{} as f64", program_args[i]))
.into(),
),
}).collect::<Vec<RuntimeValue>>()
};
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:
// - a name for the module
// - a module declaration
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
let main = ModuleInstance::new(&loaded_module, &ImportsBuilder::default())
.expect("Failed to instantiate module")
.run_start(&mut NopExternals)
.expect("Failed to run start function in module");
// Intialize deserialized module. It adds module into It expects 3 parameters:
// - a name for the module
// - a module declaration
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
let main = ModuleInstance::new(&loaded_module, &ImportsBuilder::default())
.expect("Failed to instantiate module")
.run_start(&mut NopExternals)
.expect("Failed to run start function in module");
println!("Result: {:?}", main.invoke_export(func_name, &args, &mut NopExternals).expect(""));
println!(
"Result: {:?}",
main.invoke_export(func_name, &args, &mut NopExternals)
.expect("")
);
}

View File

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

View File

@ -5,12 +5,12 @@ extern crate wasmi;
use std::env::args;
use std::fs::File;
use wasmi::{
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef,
ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, Signature,
TableDescriptor, TableInstance, TableRef};
use wasmi::memory_units::*;
use wasmi::{
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder,
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance,
NopExternals, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
};
fn load_from_file(filename: &str) -> Module {
use std::io::prelude::*;
@ -76,6 +76,6 @@ fn main() {
.with_resolver("asm2wasm", &ResolveAll)
.with_resolver("spectest", &ResolveAll),
).expect("Failed to instantiate module")
.run_start(&mut NopExternals)
.expect("Failed to run start function in module");
.run_start(&mut NopExternals)
.expect("Failed to run start function in module");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,10 @@
use {
Error, Signature, FuncRef, GlobalInstance, GlobalRef, ImportsBuilder, MemoryInstance,
MemoryRef, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue,
TableInstance, TableRef, Module, GlobalDescriptor, TableDescriptor, MemoryDescriptor,
};
use memory_units::Pages;
use std::fs::File;
use {
Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals,
RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
};
struct Env {
table_base: GlobalRef,
@ -60,12 +60,17 @@ impl ModuleImportResolver for Env {
}
}
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> {
fn resolve_table(
&self,
field_name: &str,
_table_type: &TableDescriptor,
) -> Result<TableRef, Error> {
match field_name {
"table" => Ok(self.table.clone()),
_ => Err(Error::Instantiation(
format!("env module doesn't provide table '{}'", field_name),
)),
_ => Err(Error::Instantiation(format!(
"env module doesn't provide table '{}'",
field_name
))),
}
}
}
@ -90,10 +95,8 @@ fn interpreter_inc_i32() {
let env = Env::new();
let instance = ModuleInstance::new(
&module,
&ImportsBuilder::new().with_resolver("env", &env),
).expect("Failed to instantiate module")
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
.expect("Failed to instantiate module")
.assert_no_start();
let i32_val = 42;
@ -109,40 +112,40 @@ fn interpreter_inc_i32() {
#[test]
fn interpreter_accumulate_u8() {
// Name of function contained in WASM file (note the leading underline)
const FUNCTION_NAME: &'static str = "_accumulate_u8";
// The WASM file containing the module and function
const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast";
// The octet sequence being accumulated
const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1];
// Name of function contained in WASM file (note the leading underline)
const FUNCTION_NAME: &'static str = "_accumulate_u8";
// The WASM file containing the module and function
const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast";
// The octet sequence being accumulated
const BUF: &[u8] = &[9, 8, 7, 6, 5, 4, 3, 2, 1];
// 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 env = Env::new();
let instance = ModuleInstance::new(
&module,
&ImportsBuilder::new().with_resolver("env", &env),
).expect("Failed to instantiate module")
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
.expect("Failed to instantiate module")
.assert_no_start();
let env_memory = env.memory.clone();
let env_memory = env.memory.clone();
// Place the octet-sequence at index 0 in linear memory
let offset: u32 = 0;
let _ = env_memory.set(offset, BUF);
// Place the octet-sequence at index 0 in linear memory
let offset: u32 = 0;
let _ = env_memory.set(offset, BUF);
// Set up the function argument list and invoke the function
let args = &[RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)];
let retval = instance
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
.expect("Failed to execute 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 retval = instance
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
.expect("Failed to execute function");
// For verification, repeat accumulation using native code
let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32);
let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu));
// For verification, repeat accumulation using native code
let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32);
let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu));
// Verify calculation from WebAssembly runtime is identical to expected result
assert_eq!(exp_retval, retval);
// Verify calculation from WebAssembly runtime is identical to expected result
assert_eq!(exp_retval, retval);
}

View File

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

View File

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

View File

@ -1,12 +1,12 @@
#[allow(unused_imports)]
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 core::u32;
use parity_wasm::elements::{BlockType, Func, FuncBody, Instruction, TableElementType, ValueType};
use validation::context::ModuleContext;
use validation::Error;
use validation::util::Locals;
use validation::Error;
use common::stack::StackWithLimit;
use isa;
@ -39,13 +39,9 @@ enum BlockFrameType {
/// Usual block frame.
///
/// Can be used for an implicit function block.
Block {
end_label: LabelId,
},
Block { end_label: LabelId },
/// Loop frame (branching to the beginning of block).
Loop {
header: LabelId,
},
Loop { header: LabelId },
/// True-subblock of if expression.
IfTrue {
/// If jump happens inside the if-true block then control will
@ -58,9 +54,7 @@ enum BlockFrameType {
if_not: LabelId,
},
/// False-subblock of if expression.
IfFalse {
end_label: LabelId,
}
IfFalse { end_label: LabelId },
}
impl BlockFrameType {
@ -183,9 +177,7 @@ impl FunctionReader {
let end_label = context.sink.new_label();
push_label(
BlockFrameType::Block {
end_label,
},
BlockFrameType::Block { end_label },
result_ty,
context.position,
&context.value_stack,
@ -198,7 +190,10 @@ impl FunctionReader {
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();
if body_len == 0 {
return Err(Error("Non-empty function body expected".into()));
@ -207,15 +202,19 @@ impl FunctionReader {
loop {
let instruction = &body[context.position];
let outcome = FunctionReader::read_instruction(context, instruction)
.map_err(|err| Error(format!("At instruction {:?}(@{}): {}", instruction, context.position, err)))?;
let outcome =
FunctionReader::read_instruction(context, instruction).map_err(|err| {
Error(format!(
"At instruction {:?}(@{}): {}",
instruction, context.position, err
))
})?;
match outcome {
Outcome::NextInstruction => (),
Outcome::Unreachable => make_top_frame_polymorphic(
&mut context.value_stack,
&mut context.frame_stack
),
Outcome::Unreachable => {
make_top_frame_polymorphic(&mut context.value_stack, &mut context.frame_stack)
}
}
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::*;
match *instruction {
// Nop instruction doesn't do anything. It is safe to just skip it.
Nop => {},
Nop => {}
Unreachable => {
context.sink.emit(isa::Instruction::Unreachable);
@ -239,9 +241,7 @@ impl FunctionReader {
Block(block_type) => {
let end_label = context.sink.new_label();
push_label(
BlockFrameType::Block {
end_label
},
BlockFrameType::Block { end_label },
block_type,
context.position,
&context.value_stack,
@ -254,9 +254,7 @@ impl FunctionReader {
context.sink.resolve_label(header);
push_label(
BlockFrameType::Loop {
header,
},
BlockFrameType::Loop { header },
block_type,
context.position,
&context.value_stack,
@ -269,12 +267,13 @@ impl FunctionReader {
let if_not = 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(
BlockFrameType::IfTrue {
if_not,
end_label,
},
BlockFrameType::IfTrue { if_not, end_label },
block_type,
context.position,
&context.value_stack,
@ -283,14 +282,15 @@ impl FunctionReader {
context.sink.emit_br_eqz(Target {
label: if_not,
drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, },
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
});
}
Else => {
let (block_type, if_not, end_label) = {
let top_frame = top_label(
&context.frame_stack,
);
let top_frame = top_label(&context.frame_stack);
let (if_not, end_label) = match top_frame.frame_type {
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).
context.sink.emit_br(Target {
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
@ -312,14 +315,9 @@ impl FunctionReader {
// Then, we pop the current label. It discards all values that pushed in the current
// frame.
pop_label(
&mut context.value_stack,
&mut context.frame_stack
)?;
pop_label(&mut context.value_stack, &mut context.frame_stack)?;
push_label(
BlockFrameType::IfFalse {
end_label,
},
BlockFrameType::IfFalse { end_label },
block_type,
context.position,
&context.value_stack,
@ -335,14 +333,10 @@ impl FunctionReader {
if let BlockFrameType::IfTrue { if_not, .. } = frame_type {
// A `if` without an `else` can't return a result.
if block_type != BlockType::NoResult {
return Err(
Error(
format!(
return Err(Error(format!(
"If block without else required to have NoResult block type. But it has {:?} type",
block_type
)
)
);
)));
}
// Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump
@ -365,7 +359,7 @@ impl FunctionReader {
tee_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into()
value_type.into(),
)?;
}
@ -388,11 +382,7 @@ impl FunctionReader {
Br(depth) => {
Validator::validate_br(context, depth)?;
let target = require_target(
depth,
&context.value_stack,
&context.frame_stack,
);
let target = require_target(depth, &context.value_stack, &context.frame_stack);
context.sink.emit_br(target);
return Ok(Outcome::Unreachable);
@ -400,11 +390,7 @@ impl FunctionReader {
BrIf(depth) => {
Validator::validate_br_if(context, depth)?;
let target = require_target(
depth,
&context.value_stack,
&context.frame_stack,
);
let target = require_target(depth, &context.value_stack, &context.frame_stack);
context.sink.emit_br_nez(target);
}
BrTable(ref table, default) => {
@ -412,32 +398,26 @@ impl FunctionReader {
let mut targets = Vec::new();
for depth in table.iter() {
let target = require_target(
*depth,
&context.value_stack,
&context.frame_stack,
);
let target = require_target(*depth, &context.value_stack, &context.frame_stack);
targets.push(target);
}
let default_target = require_target(
default,
&context.value_stack,
&context.frame_stack,
);
let default_target =
require_target(default, &context.value_stack, &context.frame_stack);
context.sink.emit_br_table(&targets, default_target);
return Ok(Outcome::Unreachable);
}
Return => {
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(
&context.locals,
&context.value_stack,
&context.frame_stack,
);
let drop_keep =
drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack);
context.sink.emit(isa::Instruction::Return(drop_keep));
return Ok(Outcome::Unreachable);
@ -464,37 +444,19 @@ impl FunctionReader {
GetLocal(index) => {
// We need to calculate relative depth before validation since
// it will change the value stack size.
let depth = relative_local_depth(
index,
&context.locals,
&context.value_stack,
)?;
let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
Validator::validate_get_local(context, index)?;
context.sink.emit(
isa::Instruction::GetLocal(depth),
);
context.sink.emit(isa::Instruction::GetLocal(depth));
}
SetLocal(index) => {
Validator::validate_set_local(context, index)?;
let depth = relative_local_depth(
index,
&context.locals,
&context.value_stack,
)?;
context.sink.emit(
isa::Instruction::SetLocal(depth),
);
let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
context.sink.emit(isa::Instruction::SetLocal(depth));
}
TeeLocal(index) => {
Validator::validate_tee_local(context, index)?;
let depth = relative_local_depth(
index,
&context.locals,
&context.value_stack,
)?;
context.sink.emit(
isa::Instruction::TeeLocal(depth),
);
let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
context.sink.emit(isa::Instruction::TeeLocal(depth));
}
GetGlobal(index) => {
Validator::validate_get_global(context, index)?;
@ -1136,78 +1098,160 @@ impl FunctionReader {
struct 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())?;
Ok(())
}
fn validate_unop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
fn validate_unop(
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())?;
Ok(())
}
fn validate_binop(context: &mut FunctionValidationContext, 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())?;
fn validate_binop(
context: &mut FunctionValidationContext,
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())?;
Ok(())
}
fn validate_testop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
fn validate_testop(
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())?;
Ok(())
}
fn validate_relop(context: &mut FunctionValidationContext, 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())?;
fn validate_relop(
context: &mut FunctionValidationContext,
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())?;
Ok(())
}
fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: ValueType, value_type2: ValueType) -> Result<(), Error> {
pop_value(&mut context.value_stack, &context.frame_stack, value_type1.into())?;
fn validate_cvtop(
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())?;
Ok(())
}
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(())
}
fn validate_select(context: &mut FunctionValidationContext) -> Result<(), Error> {
pop_value(&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,
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)?;
push_value(&mut context.value_stack, select_type)?;
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)?;
push_value(&mut context.value_stack, local_type.into())?;
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 value_type = pop_value(&mut context.value_stack, &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)));
let value_type = pop_value(
&mut context.value_stack,
&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(())
}
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)?;
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(())
}
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 = context.module.require_global(index, None)?;
global.content_type().into()
@ -1216,37 +1260,75 @@ impl Validator {
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 = context.module.require_global(index, Some(true))?;
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 {
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(())
}
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 {
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)?;
push_value(&mut context.value_stack, value_type.into())?;
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 {
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)?;
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
pop_value(
&mut context.value_stack,
&context.frame_stack,
value_type.into(),
)?;
pop_value(
&mut context.value_stack,
&context.frame_stack,
ValueType::I32.into(),
)?;
Ok(())
}
@ -1257,14 +1339,22 @@ impl Validator {
};
if !frame_type.is_loop() {
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(())
}
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 = require_label(depth, &context.frame_stack)?;
@ -1272,13 +1362,21 @@ impl Validator {
};
if !frame_type.is_loop() {
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(())
}
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 default_block = require_label(default, &context.frame_stack)?;
let required_block_type = if !default_block.frame_type.is_loop() {
@ -1295,23 +1393,26 @@ impl Validator {
BlockType::NoResult
};
if required_block_type != label_block_type {
return Err(
Error(
format!(
"Labels in br_table points to block of different types: {:?} and {:?}",
required_block_type,
label_block.block_type
)
)
);
return Err(Error(format!(
"Labels in br_table points to block of different types: {:?} and {:?}",
required_block_type, label_block.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 {
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(())
@ -1320,7 +1421,11 @@ impl Validator {
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> {
let (argument_types, return_type) = context.module.require_function(idx)?;
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 {
push_value(&mut context.value_stack, value_type.into())?;
@ -1328,7 +1433,10 @@ impl Validator {
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)?;
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)?;
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 {
push_value(&mut context.value_stack, value_type.into())?;
@ -1359,7 +1475,11 @@ impl Validator {
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<(), Error> {
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())?;
Ok(())
}
@ -1416,7 +1536,9 @@ fn make_top_frame_polymorphic(
value_stack: &mut StackWithLimit<StackValueType>,
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);
frame.polymorphic_stack = true;
}
@ -1501,7 +1623,11 @@ fn pop_label(
match block_type {
BlockType::NoResult => (),
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 {
frame_stack.top()
frame_stack
.top()
.expect("this function can't be called with empty frame stack")
}
@ -1534,8 +1661,7 @@ fn require_target(
value_stack: &StackWithLimit<StackValueType>,
frame_stack: &StackWithLimit<BlockFrame>,
) -> Target {
let is_stack_polymorphic = top_label(frame_stack)
.polymorphic_stack;
let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack;
let frame =
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(
idx: u32,
locals: &Locals,
value_stack: &StackWithLimit<StackValueType>
value_stack: &StackWithLimit<StackValueType>,
) -> Result<u32, Error> {
let value_stack_height = value_stack.len() as u32;
let locals_and_params_count = locals.count();
@ -1612,9 +1738,7 @@ fn relative_local_depth(
let depth = value_stack_height
.checked_add(locals_and_params_count)
.and_then(|x| x.checked_sub(idx))
.ok_or_else(||
Error(String::from("Locals range not in 32-bit range"))
)?;
.ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
Ok(depth)
}
@ -1655,12 +1779,15 @@ impl Sink {
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] {
(Label::Resolved(dst_pc), _) => dst_pc,
(Label::NotResolved, ref mut unresolved) => {
unresolved
.push(reloc_creator());
unresolved.push(reloc_creator());
u32::max_value()
}
}
@ -1671,10 +1798,7 @@ impl Sink {
}
fn emit_br(&mut self, target: Target) {
let Target {
label,
drop_keep,
} = target;
let Target { label, drop_keep } = target;
let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
self.ins.push(isa::Instruction::Br(isa::Target {
@ -1684,10 +1808,7 @@ impl Sink {
}
fn emit_br_eqz(&mut self, target: Target) {
let Target {
label,
drop_keep,
} = target;
let Target { label, drop_keep } = target;
let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
self.ins.push(isa::Instruction::BrIfEqz(isa::Target {
@ -1697,10 +1818,7 @@ impl Sink {
}
fn emit_br_nez(&mut self, target: Target) {
let Target {
label,
drop_keep,
} = target;
let Target { label, drop_keep } = target;
let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
self.ins.push(isa::Instruction::BrIfNez(isa::Target {
@ -1714,26 +1832,23 @@ impl Sink {
let pc = self.cur_pc();
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 });
isa_targets.push(
isa::Target {
dst_pc,
drop_keep: drop_keep.into(),
},
);
isa_targets.push(isa::Target {
dst_pc,
drop_keep: drop_keep.into(),
});
}
self.ins.push(isa::Instruction::BrTable(
isa_targets.into_boxed_slice(),
));
self.ins
.push(isa::Instruction::BrTable(isa_targets.into_boxed_slice()));
}
/// Create a new unresolved label.
fn new_label(&mut self) -> LabelId {
let label_idx = self.labels.len();
self.labels.push(
(Label::NotResolved, Vec::new()),
);
self.labels.push((Label::NotResolved, Vec::new()));
LabelId(label_idx)
}
@ -1762,14 +1877,18 @@ impl Sink {
/// Consume this Sink and returns isa::Instructions.
fn into_inner(self) -> isa::Instructions {
// At this moment all labels should be resolved.
assert!({
self.labels.iter().all(|(state, unresolved)|
match (state, unresolved) {
(Label::Resolved(_), unresolved) if unresolved.is_empty() => true,
_ => false,
}
)
}, "there are unresolved labels left: {:?}", self.labels);
assert!(
{
self.labels
.iter()
.all(|(state, unresolved)| match (state, unresolved) {
(Label::Resolved(_), unresolved) if unresolved.is_empty() => true,
_ => false,
})
},
"there are unresolved labels left: {:?}",
self.labels
);
self.ins
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,16 @@
#![cfg(test)]
use std::fs::File;
use std::collections::HashMap;
use std::fs::File;
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
use wasmi::memory_units::Pages;
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap};
use wasmi::{
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance,
GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature,
TableDescriptor, TableInstance, TableRef, Trap,
};
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
match val {
@ -21,30 +23,30 @@ fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
#[derive(Debug)]
enum Error {
Load(String),
Start(Trap),
Script(script::Error),
Interpreter(InterpreterError),
Load(String),
Start(Trap),
Script(script::Error),
Interpreter(InterpreterError),
}
impl From<InterpreterError> for Error {
fn from(e: InterpreterError) -> Error {
Error::Interpreter(e)
}
fn from(e: InterpreterError) -> Error {
Error::Interpreter(e)
}
}
impl From<script::Error> for Error {
fn from(e: script::Error) -> Error {
Error::Script(e)
}
fn from(e: script::Error) -> Error {
Error::Script(e)
}
}
struct SpecModule {
table: TableRef,
memory: MemoryRef,
global_i32: GlobalRef,
global_f32: GlobalRef,
global_f64: GlobalRef,
table: TableRef,
memory: MemoryRef,
global_i32: GlobalRef,
global_f32: GlobalRef,
global_f64: GlobalRef,
}
impl SpecModule {
@ -62,27 +64,27 @@ impl SpecModule {
const PRINT_FUNC_INDEX: usize = 0;
impl Externals for SpecModule {
fn invoke_index(
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> {
match index {
PRINT_FUNC_INDEX => {
println!("print: {:?}", args);
Ok(None)
}
_ => panic!("SpecModule doesn't provide function at index {}", index),
}
}
fn invoke_index(
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> {
match index {
PRINT_FUNC_INDEX => {
println!("print: {:?}", args);
Ok(None)
}
_ => panic!("SpecModule doesn't provide function at index {}", index),
}
}
}
impl ModuleImportResolver for SpecModule {
fn resolve_func(
&self,
field_name: &str,
func_type: &Signature,
) -> Result<FuncRef, InterpreterError> {
fn resolve_func(
&self,
field_name: &str,
func_type: &Signature,
) -> Result<FuncRef, InterpreterError> {
let index = match field_name {
"print" => 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);
}
}
fn resolve_global(
&self,
field_name: &str,
_global_type: &GlobalDescriptor,
) -> Result<GlobalRef, InterpreterError> {
fn resolve_global(
&self,
field_name: &str,
_global_type: &GlobalDescriptor,
) -> Result<GlobalRef, InterpreterError> {
match field_name {
"global_i32" => Ok(self.global_i32.clone()),
"global_f32" => Ok(self.global_f32.clone()),
@ -190,7 +192,8 @@ impl SpecDriver {
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
match name {
Some(name) => self.module(name),
None => self.last_module
None => self
.last_module
.clone()
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
}
@ -301,7 +304,8 @@ fn run_action(
"Expected program to have loaded module {:?}",
module
));
let vec_args = args.iter()
let vec_args = args
.iter()
.cloned()
.map(spec_to_runtime_value)
.collect::<Vec<_>>();
@ -322,8 +326,7 @@ fn run_action(
.export_by_name(&field)
.ok_or_else(|| {
InterpreterError::Global(format!("Expected to have export with name {}", field))
})?
.as_global()
})?.as_global()
.cloned()
.ok_or_else(|| {
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;
let mut spec_source = Vec::new();
let mut spec_file = File::open(&spec_script_path).expect("Can't open file");
spec_file.read_to_end(&mut spec_source).expect("Can't read file");
spec_file
.read_to_end(&mut spec_source)
.expect("Can't read file");
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)).expect("Can't read spec script");
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name))
.expect("Can't read spec script");
let mut errors = vec![];
while let Some(Command { kind, line }) = parser.next()? {
@ -435,7 +441,7 @@ fn try_spec(name: &str) -> Result<(), Error> {
let result = run_action(&mut spec_driver, &action);
match result {
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
Err(_e) => {},
Err(_e) => {}
}
}
CommandKind::AssertTrap { action, .. } => {
@ -456,13 +462,13 @@ fn try_spec(name: &str) -> Result<(), Error> {
let module_load = try_load(&module.into_vec(), &mut spec_driver);
match module_load {
Ok(_) => panic!("Expected invalid module definition, got some module!"),
Err(_e) => {},
Err(_e) => {}
}
}
CommandKind::AssertUninstantiable { module, .. } => {
match try_load(&module.into_vec(), &mut spec_driver) {
Ok(_) => panic!("Expected error running start function at line {}", line),
Err(_e) => {},
Err(_e) => {}
}
}
CommandKind::Register { name, as_name, .. } => {

View File

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