rustfmt (#151)
This commit is contained in:
parent
da558c7ce7
commit
899cc32e45
|
@ -1,7 +1,6 @@
|
|||
use std::env;
|
||||
use std::process;
|
||||
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=./wasm-kernel/");
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
use std::fs::File;
|
||||
use wasmi::{ModuleInstance, NopExternals, RuntimeValue, ImportsBuilder, Module};
|
||||
use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue};
|
||||
|
||||
fn load_from_file(filename: &str) -> Module {
|
||||
use std::io::prelude::*;
|
||||
|
@ -40,5 +40,8 @@ fn main() {
|
|||
let argument: i32 = args[2].parse().expect("Integer argument required");
|
||||
|
||||
// "_call" export of function to be executed with an i32 argument and prints the result of execution
|
||||
println!("Result: {:?}", main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals));
|
||||
println!(
|
||||
"Result: {:?}",
|
||||
main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,9 +3,8 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
|
||||
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType};
|
||||
use wasmi::{RuntimeValue, ModuleInstance, NopExternals, ImportsBuilder};
|
||||
|
||||
use parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType};
|
||||
use wasmi::{ImportsBuilder, ModuleInstance, NopExternals, RuntimeValue};
|
||||
|
||||
fn main() {
|
||||
let args: Vec<_> = args().collect();
|
||||
|
@ -23,14 +22,19 @@ fn main() {
|
|||
// Export section has an entry with a func_name with an index inside a module
|
||||
let export_section = module.export_section().expect("No export section found");
|
||||
// It's a section with function declarations (which are references to the type section entries)
|
||||
let function_section = module.function_section().expect("No function section found");
|
||||
let function_section = module
|
||||
.function_section()
|
||||
.expect("No function section found");
|
||||
// Type section stores function types which are referenced by function_section entries
|
||||
let type_section = module.type_section().expect("No type section found");
|
||||
|
||||
// Given function name used to find export section entry which contains
|
||||
// an `internal` field which points to the index in the function index space
|
||||
let found_entry = export_section.entries().iter()
|
||||
.find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name));
|
||||
let found_entry = export_section
|
||||
.entries()
|
||||
.iter()
|
||||
.find(|entry| func_name == entry.field())
|
||||
.expect(&format!("No export with name {} found", func_name));
|
||||
|
||||
// Function index in the function index space (internally-defined + imported)
|
||||
let function_index: usize = match found_entry.internal() {
|
||||
|
@ -41,11 +45,14 @@ fn main() {
|
|||
// We need to count import section entries (functions only!) to subtract it from function_index
|
||||
// and obtain the index within the function section
|
||||
let import_section_len: usize = match module.import_section() {
|
||||
Some(import) =>
|
||||
import.entries().iter().filter(|entry| match entry.external() {
|
||||
Some(import) => import
|
||||
.entries()
|
||||
.iter()
|
||||
.filter(|entry| match entry.external() {
|
||||
&External::Function(_) => true,
|
||||
_ => false,
|
||||
}).count(),
|
||||
})
|
||||
.count(),
|
||||
None => 0,
|
||||
};
|
||||
|
||||
|
@ -53,7 +60,8 @@ fn main() {
|
|||
let function_index_in_section = function_index - import_section_len;
|
||||
|
||||
// Getting a type reference from a function section entry
|
||||
let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize;
|
||||
let func_type_ref: usize =
|
||||
function_section.entries()[function_index_in_section].type_ref() as usize;
|
||||
|
||||
// Use the reference to get an actual function type
|
||||
let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
|
||||
|
@ -61,12 +69,35 @@ fn main() {
|
|||
};
|
||||
|
||||
// Parses arguments and constructs runtime values in correspondence of their types
|
||||
function_type.params().iter().enumerate().map(|(i, value)| match value {
|
||||
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
|
||||
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
|
||||
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i])).into()),
|
||||
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i])).into()),
|
||||
}).collect::<Vec<RuntimeValue>>()
|
||||
function_type
|
||||
.params()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, value)| match value {
|
||||
&ValueType::I32 => RuntimeValue::I32(
|
||||
program_args[i]
|
||||
.parse::<i32>()
|
||||
.expect(&format!("Can't parse arg #{} as i32", program_args[i])),
|
||||
),
|
||||
&ValueType::I64 => RuntimeValue::I64(
|
||||
program_args[i]
|
||||
.parse::<i64>()
|
||||
.expect(&format!("Can't parse arg #{} as i64", program_args[i])),
|
||||
),
|
||||
&ValueType::F32 => RuntimeValue::F32(
|
||||
program_args[i]
|
||||
.parse::<f32>()
|
||||
.expect(&format!("Can't parse arg #{} as f32", program_args[i]))
|
||||
.into(),
|
||||
),
|
||||
&ValueType::F64 => RuntimeValue::F64(
|
||||
program_args[i]
|
||||
.parse::<f64>()
|
||||
.expect(&format!("Can't parse arg #{} as f64", program_args[i]))
|
||||
.into(),
|
||||
),
|
||||
})
|
||||
.collect::<Vec<RuntimeValue>>()
|
||||
};
|
||||
|
||||
let loaded_module = wasmi::Module::from_parity_wasm_module(module).expect("Module to be valid");
|
||||
|
@ -81,5 +112,9 @@ fn main() {
|
|||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
|
||||
println!("Result: {:?}", main.invoke_export(func_name, &args, &mut NopExternals).expect(""));
|
||||
println!(
|
||||
"Result: {:?}",
|
||||
main.invoke_export(func_name, &args, &mut NopExternals)
|
||||
.expect("")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,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)
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
use std::fs::File;
|
||||
use wasmi::{
|
||||
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef,
|
||||
ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
|
||||
ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, Signature,
|
||||
TableDescriptor, TableInstance, TableRef};
|
||||
use wasmi::memory_units::*;
|
||||
use wasmi::{
|
||||
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder,
|
||||
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance,
|
||||
NopExternals, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
|
||||
};
|
||||
|
||||
fn load_from_file(filename: &str) -> Module {
|
||||
use std::io::prelude::*;
|
||||
|
@ -46,7 +46,8 @@ impl ModuleImportResolver for ResolveAll {
|
|||
Ok(MemoryInstance::alloc(
|
||||
Pages(memory_type.initial() as usize),
|
||||
memory_type.maximum().map(|m| Pages(m as usize)),
|
||||
).unwrap())
|
||||
)
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
|
@ -75,7 +76,8 @@ fn main() {
|
|||
.with_resolver("global.Math", &ResolveAll)
|
||||
.with_resolver("asm2wasm", &ResolveAll)
|
||||
.with_resolver("spectest", &ResolveAll),
|
||||
).expect("Failed to instantiate module")
|
||||
)
|
||||
.expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
pub mod stack;
|
||||
|
||||
/// Index of default linear memory.
|
||||
|
|
|
@ -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> {
|
||||
|
|
60
src/func.rs
60
src/func.rs
|
@ -2,14 +2,14 @@
|
|||
use alloc::prelude::*;
|
||||
use alloc::rc::{Rc, Weak};
|
||||
use core::fmt;
|
||||
use parity_wasm::elements::Local;
|
||||
use {Trap, Signature};
|
||||
use host::Externals;
|
||||
use runner::{check_function_args, Interpreter, InterpreterState};
|
||||
use value::RuntimeValue;
|
||||
use types::ValueType;
|
||||
use module::ModuleInstance;
|
||||
use isa;
|
||||
use module::ModuleInstance;
|
||||
use parity_wasm::elements::Local;
|
||||
use runner::{check_function_args, Interpreter, InterpreterState};
|
||||
use types::ValueType;
|
||||
use value::RuntimeValue;
|
||||
use {Signature, Trap};
|
||||
|
||||
/// Reference to a function (See [`FuncInstance`] for details).
|
||||
///
|
||||
|
@ -58,17 +58,10 @@ pub(crate) enum FuncInstanceInternal {
|
|||
impl fmt::Debug for FuncInstance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.as_internal() {
|
||||
&FuncInstanceInternal::Internal {
|
||||
ref signature,
|
||||
..
|
||||
} => {
|
||||
&FuncInstanceInternal::Internal { ref signature, .. } => {
|
||||
// We can't write description of self.module here, because it generate
|
||||
// debug string for function instances and this will lead to infinite loop.
|
||||
write!(
|
||||
f,
|
||||
"Internal {{ signature={:?} }}",
|
||||
signature,
|
||||
)
|
||||
write!(f, "Internal {{ signature={:?} }}", signature,)
|
||||
}
|
||||
&FuncInstanceInternal::Host { ref signature, .. } => {
|
||||
write!(f, "Host {{ signature={:?} }}", signature)
|
||||
|
@ -186,15 +179,13 @@ impl FuncInstance {
|
|||
FuncInstanceInternal::Host {
|
||||
ref host_func_index,
|
||||
..
|
||||
} => {
|
||||
Ok(FuncInvocation {
|
||||
} => Ok(FuncInvocation {
|
||||
kind: FuncInvocationKind::Host {
|
||||
args,
|
||||
host_func_index: *host_func_index,
|
||||
finished: false,
|
||||
},
|
||||
})
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +230,7 @@ enum FuncInvocationKind<'args> {
|
|||
Host {
|
||||
args: &'args [RuntimeValue],
|
||||
host_func_index: usize,
|
||||
finished: bool
|
||||
finished: bool,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -255,32 +246,37 @@ impl<'args> FuncInvocation<'args> {
|
|||
/// If the invocation is resumable, the expected return value type to be feed back in.
|
||||
pub fn resumable_value_type(&self) -> Option<ValueType> {
|
||||
match &self.kind {
|
||||
&FuncInvocationKind::Internal(ref interpreter) => {
|
||||
match interpreter.state() {
|
||||
&FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
|
||||
&InterpreterState::Resumable(ref value_type) => value_type.clone(),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
&FuncInvocationKind::Host { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the invocation execution.
|
||||
pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
pub fn start_execution<'externals, E: Externals + 'externals>(
|
||||
&mut self,
|
||||
externals: &'externals mut E,
|
||||
) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
match self.kind {
|
||||
FuncInvocationKind::Internal(ref mut interpreter) => {
|
||||
if interpreter.state() != &InterpreterState::Initialized {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
Ok(interpreter.start_execution(externals)?)
|
||||
},
|
||||
FuncInvocationKind::Host { ref args, ref mut finished, ref host_func_index } => {
|
||||
}
|
||||
FuncInvocationKind::Host {
|
||||
ref args,
|
||||
ref mut finished,
|
||||
ref host_func_index,
|
||||
} => {
|
||||
if *finished {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
*finished = true;
|
||||
Ok(externals.invoke_index(*host_func_index, args.clone().into())?)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,17 +288,21 @@ impl<'args> FuncInvocation<'args> {
|
|||
///
|
||||
/// [`resumable_value_type`]: #method.resumable_value_type
|
||||
/// [`is_resumable`]: #method.is_resumable
|
||||
pub fn resume_execution<'externals, E: Externals + 'externals>(&mut self, return_val: Option<RuntimeValue>, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
pub fn resume_execution<'externals, E: Externals + 'externals>(
|
||||
&mut self,
|
||||
return_val: Option<RuntimeValue>,
|
||||
externals: &'externals mut E,
|
||||
) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
match self.kind {
|
||||
FuncInvocationKind::Internal(ref mut interpreter) => {
|
||||
if !interpreter.state().is_resumable() {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
Ok(interpreter.resume_execution(return_val, externals)?)
|
||||
},
|
||||
}
|
||||
FuncInvocationKind::Host { .. } => {
|
||||
return Err(ResumableError::NotResumable);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
|
|
24
src/host.rs
24
src/host.rs
|
@ -1,6 +1,6 @@
|
|||
use core::any::TypeId;
|
||||
use value::{RuntimeValue, FromRuntimeValue};
|
||||
use {TrapKind, Trap};
|
||||
use value::{FromRuntimeValue, RuntimeValue};
|
||||
use {Trap, TrapKind};
|
||||
|
||||
/// Wrapper around slice of [`RuntimeValue`] for using it
|
||||
/// as an argument list conveniently.
|
||||
|
@ -27,8 +27,14 @@ impl<'a> RuntimeArgs<'a> {
|
|||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if cast is invalid or not enough arguments.
|
||||
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap> where T: FromRuntimeValue {
|
||||
Ok(self.nth_value_checked(idx)?.try_into().ok_or_else(|| TrapKind::UnexpectedSignature)?)
|
||||
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap>
|
||||
where
|
||||
T: FromRuntimeValue,
|
||||
{
|
||||
Ok(self
|
||||
.nth_value_checked(idx)?
|
||||
.try_into()
|
||||
.ok_or_else(|| TrapKind::UnexpectedSignature)?)
|
||||
}
|
||||
|
||||
/// Extract argument as a [`RuntimeValue`] by index `idx`.
|
||||
|
@ -50,7 +56,10 @@ impl<'a> RuntimeArgs<'a> {
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if cast is invalid or not enough arguments.
|
||||
pub fn nth<T>(&self, idx: usize) -> T where T: FromRuntimeValue {
|
||||
pub fn nth<T>(&self, idx: usize) -> T
|
||||
where
|
||||
T: FromRuntimeValue,
|
||||
{
|
||||
let value = self.nth_value_checked(idx).expect("Invalid argument index");
|
||||
value.try_into().expect("Unexpected argument type")
|
||||
}
|
||||
|
@ -231,8 +240,8 @@ impl Externals for NopExternals {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{HostError, RuntimeArgs};
|
||||
use value::RuntimeValue;
|
||||
use super::{RuntimeArgs, HostError};
|
||||
|
||||
#[test]
|
||||
fn i32_runtime_args() {
|
||||
|
@ -248,6 +257,5 @@ mod tests {
|
|||
}
|
||||
|
||||
// Tests that `HostError` trait is object safe.
|
||||
fn _host_error_is_object_safe(_: &HostError) {
|
||||
}
|
||||
fn _host_error_is_object_safe(_: &HostError) {}
|
||||
}
|
||||
|
|
112
src/imports.rs
112
src/imports.rs
|
@ -1,20 +1,19 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashMap;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
use func::FuncRef;
|
||||
use global::GlobalRef;
|
||||
use memory::MemoryRef;
|
||||
use func::FuncRef;
|
||||
use table::TableRef;
|
||||
use module::ModuleRef;
|
||||
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||
use table::TableRef;
|
||||
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
|
||||
use {Error, Signature};
|
||||
|
||||
|
||||
/// Resolver of a module's dependencies.
|
||||
///
|
||||
/// A module have dependencies in a form of a list of imports (i.e.
|
||||
|
@ -27,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)))?)
|
||||
}
|
||||
}
|
||||
|
|
62
src/lib.rs
62
src/lib.rs
|
@ -95,9 +95,7 @@
|
|||
//! ```
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
//// alloc is required in no_std
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
|
@ -117,11 +115,11 @@ extern crate wabt;
|
|||
#[macro_use]
|
||||
extern crate assert_matches;
|
||||
|
||||
extern crate parity_wasm;
|
||||
extern crate byteorder;
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate hashmap_core;
|
||||
extern crate memory_units as memory_units_crate;
|
||||
extern crate parity_wasm;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
@ -292,7 +290,7 @@ impl Error {
|
|||
Error::Trap(ref trap) => match *trap.kind() {
|
||||
TrapKind::Host(ref host_err) => Some(&**host_err),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -347,13 +345,19 @@ impl error::Error for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl<U> From<U> for Error where U: host::HostError + Sized {
|
||||
impl<U> From<U> for Error
|
||||
where
|
||||
U: host::HostError + Sized,
|
||||
{
|
||||
fn from(e: U) -> Self {
|
||||
Error::Host(Box::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> From<U> for Trap where U: host::HostError + Sized {
|
||||
impl<U> From<U> for Trap
|
||||
where
|
||||
U: host::HostError + Sized,
|
||||
{
|
||||
fn from(e: U) -> Self {
|
||||
Trap::new(TrapKind::Host(Box::new(e)))
|
||||
}
|
||||
|
@ -377,38 +381,38 @@ impl From<validation::Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
mod validation;
|
||||
mod common;
|
||||
mod memory;
|
||||
mod module;
|
||||
mod runner;
|
||||
mod table;
|
||||
mod value;
|
||||
mod func;
|
||||
mod global;
|
||||
mod host;
|
||||
mod imports;
|
||||
mod global;
|
||||
mod func;
|
||||
mod types;
|
||||
mod isa;
|
||||
mod memory;
|
||||
mod module;
|
||||
pub mod nan_preserving_float;
|
||||
mod runner;
|
||||
mod table;
|
||||
mod types;
|
||||
mod validation;
|
||||
mod value;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
|
||||
pub use self::table::{TableInstance, TableRef};
|
||||
pub use self::value::{FromRuntimeValue, RuntimeValue, LittleEndianConvert, Error as ValueError};
|
||||
pub use self::host::{Externals, NopExternals, HostError, RuntimeArgs};
|
||||
pub use self::imports::{ModuleImportResolver, ImportResolver, ImportsBuilder};
|
||||
pub use self::module::{ModuleInstance, ModuleRef, ExternVal, NotStartedModuleRef};
|
||||
pub use self::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError};
|
||||
pub use self::global::{GlobalInstance, GlobalRef};
|
||||
pub use self::func::{FuncInstance, FuncRef, FuncInvocation, ResumableError};
|
||||
pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||
pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs};
|
||||
pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver};
|
||||
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
|
||||
pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef};
|
||||
pub use self::table::{TableInstance, TableRef};
|
||||
pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType};
|
||||
pub use self::value::{Error as ValueError, FromRuntimeValue, LittleEndianConvert, RuntimeValue};
|
||||
|
||||
/// WebAssembly-specific sizes and units.
|
||||
pub mod memory_units {
|
||||
pub use memory_units_crate::wasm32::*;
|
||||
pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of};
|
||||
pub use memory_units_crate::{size_of, ByteSize, Bytes, RoundUpTo};
|
||||
}
|
||||
|
||||
/// Deserialized module prepared for instantiation.
|
||||
|
@ -452,15 +456,9 @@ impl Module {
|
|||
/// ```
|
||||
pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> {
|
||||
use validation::{validate_module, ValidatedModule};
|
||||
let ValidatedModule {
|
||||
code_map,
|
||||
module,
|
||||
} = validate_module(module)?;
|
||||
let ValidatedModule { code_map, module } = validate_module(module)?;
|
||||
|
||||
Ok(Module {
|
||||
code_map,
|
||||
module,
|
||||
})
|
||||
Ok(Module { code_map, module })
|
||||
}
|
||||
|
||||
/// Fail if the module contains any floating-point operations
|
||||
|
|
208
src/memory.rs
208
src/memory.rs
|
@ -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.
|
||||
///
|
||||
|
@ -174,7 +174,8 @@ impl MemoryInstance {
|
|||
/// Get value from memory at given offset.
|
||||
pub fn get_value<T: LittleEndianConvert>(&self, offset: u32) -> Result<T, Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
|
||||
let region =
|
||||
self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
|
||||
Ok(T::from_little_endian(&buffer[region.range()]).expect("Slice size is checked"))
|
||||
}
|
||||
|
||||
|
@ -208,7 +209,9 @@ impl MemoryInstance {
|
|||
/// Copy data in the memory at given offset.
|
||||
pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let range = self.checked_region(&mut buffer, offset as usize, value.len())?.range();
|
||||
let range = self
|
||||
.checked_region(&mut buffer, offset as usize, value.len())?
|
||||
.range();
|
||||
|
||||
buffer[range].copy_from_slice(value);
|
||||
|
||||
|
@ -218,7 +221,9 @@ impl MemoryInstance {
|
|||
/// Copy value in the memory at given offset.
|
||||
pub fn set_value<T: LittleEndianConvert>(&self, offset: u32, value: T) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let range = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?.range();
|
||||
let range = self
|
||||
.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?
|
||||
.range();
|
||||
value.into_little_endian(&mut buffer[range]);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -255,18 +260,33 @@ impl MemoryInstance {
|
|||
Ok(size_before_grow)
|
||||
}
|
||||
|
||||
fn checked_region<B>(&self, buffer: &mut B, offset: usize, size: usize) -> Result<CheckedRegion, Error>
|
||||
where B: ::core::ops::DerefMut<Target=Vec<u8>>
|
||||
fn checked_region<B>(
|
||||
&self,
|
||||
buffer: &mut B,
|
||||
offset: usize,
|
||||
size: usize,
|
||||
) -> Result<CheckedRegion, Error>
|
||||
where
|
||||
B: ::core::ops::DerefMut<Target = Vec<u8>>,
|
||||
{
|
||||
let end = offset.checked_add(size)
|
||||
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;
|
||||
let end = offset.checked_add(size).ok_or_else(|| {
|
||||
Error::Memory(format!(
|
||||
"trying to access memory block of size {} from offset {}",
|
||||
size, offset
|
||||
))
|
||||
})?;
|
||||
|
||||
if end <= self.current_size.get() && buffer.len() < end {
|
||||
buffer.resize(end, 0);
|
||||
}
|
||||
|
||||
if end > buffer.len() {
|
||||
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len())));
|
||||
return Err(Error::Memory(format!(
|
||||
"trying to access region [{}..{}] in memory [0..{}]",
|
||||
offset,
|
||||
end,
|
||||
buffer.len()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(CheckedRegion {
|
||||
|
@ -275,15 +295,30 @@ impl MemoryInstance {
|
|||
})
|
||||
}
|
||||
|
||||
fn checked_region_pair<B>(&self, buffer: &mut B, offset1: usize, size1: usize, offset2: usize, size2: usize)
|
||||
-> Result<(CheckedRegion, CheckedRegion), Error>
|
||||
where B: ::core::ops::DerefMut<Target=Vec<u8>>
|
||||
fn checked_region_pair<B>(
|
||||
&self,
|
||||
buffer: &mut B,
|
||||
offset1: usize,
|
||||
size1: usize,
|
||||
offset2: usize,
|
||||
size2: usize,
|
||||
) -> Result<(CheckedRegion, CheckedRegion), Error>
|
||||
where
|
||||
B: ::core::ops::DerefMut<Target = Vec<u8>>,
|
||||
{
|
||||
let end1 = offset1.checked_add(size1)
|
||||
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size1, offset1)))?;
|
||||
let end1 = offset1.checked_add(size1).ok_or_else(|| {
|
||||
Error::Memory(format!(
|
||||
"trying to access memory block of size {} from offset {}",
|
||||
size1, offset1
|
||||
))
|
||||
})?;
|
||||
|
||||
let end2 = offset2.checked_add(size2)
|
||||
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size2, offset2)))?;
|
||||
let end2 = offset2.checked_add(size2).ok_or_else(|| {
|
||||
Error::Memory(format!(
|
||||
"trying to access memory block of size {} from offset {}",
|
||||
size2, offset2
|
||||
))
|
||||
})?;
|
||||
|
||||
let max = cmp::max(end1, end2);
|
||||
if max <= self.current_size.get() && buffer.len() < max {
|
||||
|
@ -291,16 +326,32 @@ impl MemoryInstance {
|
|||
}
|
||||
|
||||
if end1 > buffer.len() {
|
||||
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset1, end1, buffer.len())));
|
||||
return Err(Error::Memory(format!(
|
||||
"trying to access region [{}..{}] in memory [0..{}]",
|
||||
offset1,
|
||||
end1,
|
||||
buffer.len()
|
||||
)));
|
||||
}
|
||||
|
||||
if end2 > buffer.len() {
|
||||
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset2, end2, buffer.len())));
|
||||
return Err(Error::Memory(format!(
|
||||
"trying to access region [{}..{}] in memory [0..{}]",
|
||||
offset2,
|
||||
end2,
|
||||
buffer.len()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok((
|
||||
CheckedRegion { offset: offset1, size: size1 },
|
||||
CheckedRegion { offset: offset2, size: size2 },
|
||||
CheckedRegion {
|
||||
offset: offset1,
|
||||
size: size1,
|
||||
},
|
||||
CheckedRegion {
|
||||
offset: offset2,
|
||||
size: size2,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -314,13 +365,16 @@ impl MemoryInstance {
|
|||
pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
let (read_region, write_region) =
|
||||
self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
|
||||
unsafe { ::core::ptr::copy(
|
||||
unsafe {
|
||||
::core::ptr::copy(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
)}
|
||||
)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -336,20 +390,30 @@ impl MemoryInstance {
|
|||
///
|
||||
/// - either of specified regions is out of bounds,
|
||||
/// - these regions overlaps.
|
||||
pub fn copy_nonoverlapping(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
|
||||
pub fn copy_nonoverlapping(
|
||||
&self,
|
||||
src_offset: usize,
|
||||
dst_offset: usize,
|
||||
len: usize,
|
||||
) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
let (read_region, write_region) =
|
||||
self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
|
||||
if read_region.intersects(&write_region) {
|
||||
return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions")))
|
||||
return Err(Error::Memory(format!(
|
||||
"non-overlapping copy is used for overlapping regions"
|
||||
)));
|
||||
}
|
||||
|
||||
unsafe { ::core::ptr::copy_nonoverlapping(
|
||||
unsafe {
|
||||
::core::ptr::copy_nonoverlapping(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
)}
|
||||
)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -357,7 +421,13 @@ impl MemoryInstance {
|
|||
/// Copy memory between two (possibly distinct) memory instances.
|
||||
///
|
||||
/// If the same memory instance passed as `src` and `dst` then usual `copy` will be used.
|
||||
pub fn transfer(src: &MemoryRef, src_offset: usize, dst: &MemoryRef, dst_offset: usize, len: usize) -> Result<(), Error> {
|
||||
pub fn transfer(
|
||||
src: &MemoryRef,
|
||||
src_offset: usize,
|
||||
dst: &MemoryRef,
|
||||
dst_offset: usize,
|
||||
len: usize,
|
||||
) -> Result<(), Error> {
|
||||
if Rc::ptr_eq(&src.0, &dst.0) {
|
||||
// `transfer` is invoked with with same source and destination. Let's assume that regions may
|
||||
// overlap and use `copy`.
|
||||
|
@ -369,8 +439,12 @@ impl MemoryInstance {
|
|||
let mut src_buffer = src.buffer.borrow_mut();
|
||||
let mut dst_buffer = dst.buffer.borrow_mut();
|
||||
|
||||
let src_range = src.checked_region(&mut src_buffer, src_offset, len)?.range();
|
||||
let dst_range = dst.checked_region(&mut dst_buffer, dst_offset, len)?.range();
|
||||
let src_range = src
|
||||
.checked_region(&mut src_buffer, src_offset, len)?
|
||||
.range();
|
||||
let dst_range = dst
|
||||
.checked_region(&mut dst_buffer, dst_offset, len)?
|
||||
.range();
|
||||
|
||||
dst_buffer[dst_range].copy_from_slice(&src_buffer[src_range]);
|
||||
|
||||
|
@ -388,7 +462,9 @@ impl MemoryInstance {
|
|||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
let range = self.checked_region(&mut buffer, offset, len)?.range();
|
||||
for val in &mut buffer[range] { *val = new_val }
|
||||
for val in &mut buffer[range] {
|
||||
*val = new_val
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -434,19 +510,24 @@ impl MemoryInstance {
|
|||
|
||||
pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), String> {
|
||||
if initial > LINEAR_MEMORY_MAX_PAGES {
|
||||
return Err(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
|
||||
return Err(format!(
|
||||
"initial memory size must be at most {} pages",
|
||||
LINEAR_MEMORY_MAX_PAGES.0
|
||||
));
|
||||
}
|
||||
if let Some(maximum) = maximum {
|
||||
if initial > maximum {
|
||||
return Err(format!(
|
||||
"maximum limit {} is less than minimum {}",
|
||||
maximum.0,
|
||||
initial.0,
|
||||
maximum.0, initial.0,
|
||||
));
|
||||
}
|
||||
|
||||
if maximum > LINEAR_MEMORY_MAX_PAGES {
|
||||
return Err(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
|
||||
return Err(format!(
|
||||
"maximum memory size must be at most {} pages",
|
||||
LINEAR_MEMORY_MAX_PAGES.0
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -455,10 +536,10 @@ pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), Str
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{MemoryRef, MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
|
||||
use super::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
|
||||
use memory_units::Pages;
|
||||
use std::rc::Rc;
|
||||
use Error;
|
||||
use memory_units::Pages;
|
||||
|
||||
#[test]
|
||||
fn alloc() {
|
||||
|
@ -493,11 +574,7 @@ mod tests {
|
|||
if result.is_ok() != expected_ok {
|
||||
panic!(
|
||||
"unexpected error at {}, initial={:?}, max={:?}, expected={}, result={:?}",
|
||||
index,
|
||||
initial,
|
||||
maybe_max,
|
||||
expected_ok,
|
||||
result,
|
||||
index, initial, maybe_max, expected_ok, result,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -511,7 +588,8 @@ mod tests {
|
|||
|
||||
fn create_memory(initial_content: &[u8]) -> MemoryInstance {
|
||||
let mem = MemoryInstance::new(Pages(1), Some(Pages(1)));
|
||||
mem.set(0, initial_content).expect("Successful initialize the memory");
|
||||
mem.set(0, initial_content)
|
||||
.expect("Successful initialize the memory");
|
||||
mem
|
||||
}
|
||||
|
||||
|
@ -534,7 +612,8 @@ mod tests {
|
|||
#[test]
|
||||
fn copy_nonoverlapping() {
|
||||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
mem.copy_nonoverlapping(0, 10, 10).expect("Successfully copy the elements");
|
||||
mem.copy_nonoverlapping(0, 10, 10)
|
||||
.expect("Successfully copy the elements");
|
||||
let result = mem.get(10, 10).expect("Successfully retrieve the result");
|
||||
assert_eq!(result, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
}
|
||||
|
@ -544,7 +623,7 @@ mod tests {
|
|||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let result = mem.copy_nonoverlapping(0, 4, 6);
|
||||
match result {
|
||||
Err(Error::Memory(_)) => {},
|
||||
Err(Error::Memory(_)) => {}
|
||||
_ => panic!("Expected Error::Memory(_) result, but got {:?}", result),
|
||||
}
|
||||
}
|
||||
|
@ -554,7 +633,7 @@ mod tests {
|
|||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let result = mem.copy_nonoverlapping(4, 0, 6);
|
||||
match result {
|
||||
Err(Error::Memory(_)) => {},
|
||||
Err(Error::Memory(_)) => {}
|
||||
_ => panic!("Expected Error::Memory(_), but got {:?}", result),
|
||||
}
|
||||
}
|
||||
|
@ -562,12 +641,17 @@ mod tests {
|
|||
#[test]
|
||||
fn transfer_works() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
])));
|
||||
|
||||
MemoryInstance::transfer(&src, 4, &dst, 0, 3).unwrap();
|
||||
|
||||
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
assert_eq!(dst.get(0, 10).unwrap(), &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]);
|
||||
assert_eq!(
|
||||
dst.get(0, 10).unwrap(),
|
||||
&[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -591,19 +675,25 @@ mod tests {
|
|||
#[test]
|
||||
fn transfer_oob_errors() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
])));
|
||||
|
||||
assert!(MemoryInstance::transfer(&src, 65535, &dst, 0, 3).is_err());
|
||||
|
||||
// Check that memories content left untouched
|
||||
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
assert_eq!(dst.get(0, 10).unwrap(), &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
|
||||
assert_eq!(
|
||||
dst.get(0, 10).unwrap(),
|
||||
&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear() {
|
||||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
mem.clear(0, 0x4A, 10).expect("To successfully clear the memory");
|
||||
mem.clear(0, 0x4A, 10)
|
||||
.expect("To successfully clear the memory");
|
||||
let result = mem.get(0, 10).expect("To successfully retrieve the result");
|
||||
assert_eq!(result, &[0x4A; 10]);
|
||||
}
|
||||
|
@ -611,10 +701,12 @@ mod tests {
|
|||
#[test]
|
||||
fn get_into() {
|
||||
let mem = MemoryInstance::new(Pages(1), None);
|
||||
mem.set(6, &[13, 17, 129]).expect("memory set should not fail");
|
||||
mem.set(6, &[13, 17, 129])
|
||||
.expect("memory set should not fail");
|
||||
|
||||
let mut data = [0u8; 2];
|
||||
mem.get_into(7, &mut data[..]).expect("get_into should not fail");
|
||||
mem.get_into(7, &mut data[..])
|
||||
.expect("get_into should not fail");
|
||||
|
||||
assert_eq!(data, [17, 129]);
|
||||
}
|
||||
|
|
228
src/module.rs
228
src/module.rs
|
@ -1,26 +1,26 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use Trap;
|
||||
use core::cell::RefCell;
|
||||
use core::fmt;
|
||||
use Trap;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashMap;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type};
|
||||
use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance};
|
||||
use imports::ImportResolver;
|
||||
use global::{GlobalInstance, GlobalRef};
|
||||
use func::{FuncRef, FuncBody, FuncInstance};
|
||||
use table::TableRef;
|
||||
use memory::MemoryRef;
|
||||
use host::Externals;
|
||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||
use func::{FuncBody, FuncInstance, FuncRef};
|
||||
use global::{GlobalInstance, GlobalRef};
|
||||
use host::Externals;
|
||||
use imports::ImportResolver;
|
||||
use memory::MemoryRef;
|
||||
use memory_units::Pages;
|
||||
use parity_wasm::elements::{External, InitExpr, Instruction, Internal, ResizableLimits, Type};
|
||||
use table::TableRef;
|
||||
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
|
||||
use {Error, MemoryInstance, Module, RuntimeValue, Signature, TableInstance};
|
||||
|
||||
/// Reference to a [`ModuleInstance`].
|
||||
///
|
||||
|
@ -254,9 +254,9 @@ impl ModuleInstance {
|
|||
|
||||
match (import.external(), extern_val) {
|
||||
(&External::Function(fn_type_idx), &ExternVal::Func(ref func)) => {
|
||||
let expected_fn_type = instance.signature_by_index(fn_type_idx).expect(
|
||||
"Due to validation function type should exists",
|
||||
);
|
||||
let expected_fn_type = instance
|
||||
.signature_by_index(fn_type_idx)
|
||||
.expect("Due to validation function type should exists");
|
||||
let actual_fn_type = func.signature();
|
||||
if &*expected_fn_type != actual_fn_type {
|
||||
return Err(Error::Instantiation(format!(
|
||||
|
@ -289,8 +289,7 @@ impl ModuleInstance {
|
|||
(expected_import, actual_extern_val) => {
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Expected {:?} type, but provided {:?} extern_val",
|
||||
expected_import,
|
||||
actual_extern_val
|
||||
expected_import, actual_extern_val
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -299,9 +298,10 @@ impl ModuleInstance {
|
|||
|
||||
let code = loaded_module.code();
|
||||
{
|
||||
let funcs = module.function_section().map(|fs| fs.entries()).unwrap_or(
|
||||
&[],
|
||||
);
|
||||
let funcs = module
|
||||
.function_section()
|
||||
.map(|fs| fs.entries())
|
||||
.unwrap_or(&[]);
|
||||
let bodies = module.code_section().map(|cs| cs.bodies()).unwrap_or(&[]);
|
||||
debug_assert!(
|
||||
funcs.len() == bodies.len(),
|
||||
|
@ -311,9 +311,9 @@ impl ModuleInstance {
|
|||
for (index, (ty, body)) in
|
||||
Iterator::zip(funcs.into_iter(), bodies.into_iter()).enumerate()
|
||||
{
|
||||
let signature = instance.signature_by_index(ty.type_ref()).expect(
|
||||
"Due to validation type should exists",
|
||||
);
|
||||
let signature = instance
|
||||
.signature_by_index(ty.type_ref())
|
||||
.expect("Due to validation type should exists");
|
||||
let code = code.get(index).expect(
|
||||
"At func validation time labels are collected; Collected labels are added by index; qed",
|
||||
).clone();
|
||||
|
@ -328,16 +328,15 @@ impl ModuleInstance {
|
|||
}
|
||||
|
||||
for table_type in module.table_section().map(|ts| ts.entries()).unwrap_or(&[]) {
|
||||
let table = TableInstance::alloc(
|
||||
table_type.limits().initial(),
|
||||
table_type.limits().maximum(),
|
||||
)?;
|
||||
let table =
|
||||
TableInstance::alloc(table_type.limits().initial(), table_type.limits().maximum())?;
|
||||
instance.push_table(table);
|
||||
}
|
||||
|
||||
for memory_type in module.memory_section().map(|ms| ms.entries()).unwrap_or(
|
||||
&[],
|
||||
)
|
||||
for memory_type in module
|
||||
.memory_section()
|
||||
.map(|ms| ms.entries())
|
||||
.unwrap_or(&[])
|
||||
{
|
||||
let initial: Pages = Pages(memory_type.limits().initial() as usize);
|
||||
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
|
||||
|
@ -347,46 +346,45 @@ impl ModuleInstance {
|
|||
instance.push_memory(memory);
|
||||
}
|
||||
|
||||
for global_entry in module.global_section().map(|gs| gs.entries()).unwrap_or(
|
||||
&[],
|
||||
)
|
||||
for global_entry in module
|
||||
.global_section()
|
||||
.map(|gs| gs.entries())
|
||||
.unwrap_or(&[])
|
||||
{
|
||||
let init_val = eval_init_expr(global_entry.init_expr(), &*instance);
|
||||
let global = GlobalInstance::alloc(
|
||||
init_val,
|
||||
global_entry.global_type().is_mutable(),
|
||||
);
|
||||
let global = GlobalInstance::alloc(init_val, global_entry.global_type().is_mutable());
|
||||
instance.push_global(global);
|
||||
}
|
||||
|
||||
for export in module.export_section().map(|es| es.entries()).unwrap_or(
|
||||
&[],
|
||||
)
|
||||
for export in module
|
||||
.export_section()
|
||||
.map(|es| es.entries())
|
||||
.unwrap_or(&[])
|
||||
{
|
||||
let field = export.field();
|
||||
let extern_val: ExternVal = match *export.internal() {
|
||||
Internal::Function(idx) => {
|
||||
let func = instance.func_by_index(idx).expect(
|
||||
"Due to validation func should exists",
|
||||
);
|
||||
let func = instance
|
||||
.func_by_index(idx)
|
||||
.expect("Due to validation func should exists");
|
||||
ExternVal::Func(func)
|
||||
}
|
||||
Internal::Global(idx) => {
|
||||
let global = instance.global_by_index(idx).expect(
|
||||
"Due to validation global should exists",
|
||||
);
|
||||
let global = instance
|
||||
.global_by_index(idx)
|
||||
.expect("Due to validation global should exists");
|
||||
ExternVal::Global(global)
|
||||
}
|
||||
Internal::Memory(idx) => {
|
||||
let memory = instance.memory_by_index(idx).expect(
|
||||
"Due to validation memory should exists",
|
||||
);
|
||||
let memory = instance
|
||||
.memory_by_index(idx)
|
||||
.expect("Due to validation memory should exists");
|
||||
ExternVal::Memory(memory)
|
||||
}
|
||||
Internal::Table(idx) => {
|
||||
let table = instance.table_by_index(idx).expect(
|
||||
"Due to validation table should exists",
|
||||
);
|
||||
let table = instance
|
||||
.table_by_index(idx)
|
||||
.expect("Due to validation table should exists");
|
||||
ExternVal::Table(table)
|
||||
}
|
||||
};
|
||||
|
@ -410,31 +408,34 @@ impl ModuleInstance {
|
|||
|
||||
let module_ref = ModuleInstance::alloc_module(loaded_module, extern_vals)?;
|
||||
|
||||
for element_segment in module.elements_section().map(|es| es.entries()).unwrap_or(
|
||||
&[],
|
||||
)
|
||||
for element_segment in module
|
||||
.elements_section()
|
||||
.map(|es| es.entries())
|
||||
.unwrap_or(&[])
|
||||
{
|
||||
let offset_val = match eval_init_expr(element_segment.offset(), &module_ref) {
|
||||
RuntimeValue::I32(v) => v as u32,
|
||||
_ => panic!("Due to validation elem segment offset should evaluate to i32"),
|
||||
};
|
||||
|
||||
let table_inst = module_ref.table_by_index(DEFAULT_TABLE_INDEX).expect(
|
||||
"Due to validation default table should exists",
|
||||
);
|
||||
let table_inst = module_ref
|
||||
.table_by_index(DEFAULT_TABLE_INDEX)
|
||||
.expect("Due to validation default table should exists");
|
||||
|
||||
// This check is not only for bailing out early, but also to check the case when
|
||||
// segment consist of 0 members.
|
||||
if offset_val as u64 + element_segment.members().len() as u64 > table_inst.current_size() as u64 {
|
||||
return Err(
|
||||
Error::Instantiation("elements segment does not fit".to_string())
|
||||
);
|
||||
if offset_val as u64 + element_segment.members().len() as u64
|
||||
> table_inst.current_size() as u64
|
||||
{
|
||||
return Err(Error::Instantiation(
|
||||
"elements segment does not fit".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
for (j, func_idx) in element_segment.members().into_iter().enumerate() {
|
||||
let func = module_ref.func_by_index(*func_idx).expect(
|
||||
"Due to validation funcs from element segments should exists",
|
||||
);
|
||||
let func = module_ref
|
||||
.func_by_index(*func_idx)
|
||||
.expect("Due to validation funcs from element segments should exists");
|
||||
|
||||
table_inst.set(offset_val + j as u32, Some(func))?;
|
||||
}
|
||||
|
@ -446,9 +447,9 @@ impl ModuleInstance {
|
|||
_ => panic!("Due to validation data segment offset should evaluate to i32"),
|
||||
};
|
||||
|
||||
let memory_inst = module_ref.memory_by_index(DEFAULT_MEMORY_INDEX).expect(
|
||||
"Due to validation default memory should exists",
|
||||
);
|
||||
let memory_inst = module_ref
|
||||
.memory_by_index(DEFAULT_MEMORY_INDEX)
|
||||
.expect("Due to validation default memory should exists");
|
||||
memory_inst.set(offset_val, data_segment.value())?;
|
||||
}
|
||||
|
||||
|
@ -540,17 +541,20 @@ impl ModuleInstance {
|
|||
}
|
||||
External::Table(ref table_type) => {
|
||||
let table_descriptor = TableDescriptor::from_elements(table_type);
|
||||
let table = imports.resolve_table(module_name, field_name, &table_descriptor)?;
|
||||
let table =
|
||||
imports.resolve_table(module_name, field_name, &table_descriptor)?;
|
||||
ExternVal::Table(table)
|
||||
}
|
||||
External::Memory(ref memory_type) => {
|
||||
let memory_descriptor = MemoryDescriptor::from_elements(memory_type);
|
||||
let memory = imports.resolve_memory(module_name, field_name, &memory_descriptor)?;
|
||||
let memory =
|
||||
imports.resolve_memory(module_name, field_name, &memory_descriptor)?;
|
||||
ExternVal::Memory(memory)
|
||||
}
|
||||
External::Global(ref global_type) => {
|
||||
let global_descriptor = GlobalDescriptor::from_elements(global_type);
|
||||
let global = imports.resolve_global(module_name, field_name, &global_descriptor)?;
|
||||
let global =
|
||||
imports.resolve_global(module_name, field_name, &global_descriptor)?;
|
||||
ExternVal::Global(global)
|
||||
}
|
||||
};
|
||||
|
@ -614,23 +618,21 @@ impl ModuleInstance {
|
|||
args: &[RuntimeValue],
|
||||
externals: &mut E,
|
||||
) -> Result<Option<RuntimeValue>, Error> {
|
||||
let extern_val = self.export_by_name(func_name).ok_or_else(|| {
|
||||
Error::Function(format!("Module doesn't have export {}", func_name))
|
||||
})?;
|
||||
let extern_val = self
|
||||
.export_by_name(func_name)
|
||||
.ok_or_else(|| Error::Function(format!("Module doesn't have export {}", func_name)))?;
|
||||
|
||||
let func_instance = match extern_val {
|
||||
ExternVal::Func(func_instance) => func_instance,
|
||||
unexpected => {
|
||||
return Err(Error::Function(format!(
|
||||
"Export {} is not a function, but {:?}",
|
||||
func_name,
|
||||
unexpected
|
||||
func_name, unexpected
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
FuncInstance::invoke(&func_instance, args, externals)
|
||||
.map_err(|t| Error::Trap(t))
|
||||
FuncInstance::invoke(&func_instance, args, externals).map_err(|t| Error::Trap(t))
|
||||
}
|
||||
|
||||
/// Find export by a name.
|
||||
|
@ -685,9 +687,10 @@ impl<'a> NotStartedModuleRef<'a> {
|
|||
/// Returns `Err` if start function traps.
|
||||
pub fn run_start<E: Externals>(self, state: &mut E) -> Result<ModuleRef, Trap> {
|
||||
if let Some(start_fn_idx) = self.loaded_module.module().start_section() {
|
||||
let start_func = self.instance.func_by_index(start_fn_idx).expect(
|
||||
"Due to validation start function should exists",
|
||||
);
|
||||
let start_func = self
|
||||
.instance
|
||||
.func_by_index(start_fn_idx)
|
||||
.expect("Due to validation start function should exists");
|
||||
FuncInstance::invoke(&start_func, &[], state)?;
|
||||
}
|
||||
Ok(self.instance)
|
||||
|
@ -718,9 +721,9 @@ fn eval_init_expr(init_expr: &InitExpr, module: &ModuleInstance) -> RuntimeValue
|
|||
Instruction::F32Const(v) => RuntimeValue::decode_f32(v),
|
||||
Instruction::F64Const(v) => RuntimeValue::decode_f64(v),
|
||||
Instruction::GetGlobal(idx) => {
|
||||
let global = module.global_by_index(idx).expect(
|
||||
"Due to validation global should exists in module",
|
||||
);
|
||||
let global = module
|
||||
.global_by_index(idx)
|
||||
.expect("Due to validation global should exists in module");
|
||||
global.get()
|
||||
}
|
||||
_ => panic!("Due to validation init should be a const expr"),
|
||||
|
@ -744,7 +747,7 @@ fn match_limits(l1: &ResizableLimits, l2: &ResizableLimits) -> Result<(), Error>
|
|||
"trying to import with limits l1.max={:?} and l2.max={:?}",
|
||||
l1.maximum(),
|
||||
l2.maximum()
|
||||
)))
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -765,14 +768,13 @@ pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use imports::ImportsBuilder;
|
||||
use super::{ExternVal, ModuleInstance};
|
||||
use func::FuncInstance;
|
||||
use types::{Signature, ValueType};
|
||||
use super::{ModuleInstance, ExternVal};
|
||||
use imports::ImportsBuilder;
|
||||
use tests::parse_wat;
|
||||
use types::{Signature, ValueType};
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
|
@ -782,12 +784,11 @@ mod tests {
|
|||
(module
|
||||
(func $f)
|
||||
(start $f))
|
||||
"#
|
||||
"#,
|
||||
);
|
||||
ModuleInstance::new(
|
||||
&module_with_start,
|
||||
&ImportsBuilder::default()
|
||||
).unwrap().assert_no_start();
|
||||
ModuleInstance::new(&module_with_start, &ImportsBuilder::default())
|
||||
.unwrap()
|
||||
.assert_no_start();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -800,40 +801,39 @@ mod tests {
|
|||
"#,
|
||||
);
|
||||
|
||||
assert!(
|
||||
ModuleInstance::with_externvals(
|
||||
assert!(ModuleInstance::with_externvals(
|
||||
&module_with_single_import,
|
||||
[
|
||||
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0),)
|
||||
].iter(),
|
||||
).is_ok()
|
||||
);
|
||||
[ExternVal::Func(FuncInstance::alloc_host(
|
||||
Signature::new(&[][..], None),
|
||||
0
|
||||
),)]
|
||||
.iter(),
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
// externval vector is longer than import count.
|
||||
assert!(
|
||||
ModuleInstance::with_externvals(
|
||||
assert!(ModuleInstance::with_externvals(
|
||||
&module_with_single_import,
|
||||
[
|
||||
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0)),
|
||||
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 1)),
|
||||
].iter(),
|
||||
).is_err()
|
||||
);
|
||||
]
|
||||
.iter(),
|
||||
)
|
||||
.is_err());
|
||||
|
||||
// externval vector is shorter than import count.
|
||||
assert!(ModuleInstance::with_externvals(&module_with_single_import, [].iter(),).is_err());
|
||||
|
||||
// externval vector has an unexpected type.
|
||||
assert!(
|
||||
ModuleInstance::with_externvals(
|
||||
assert!(ModuleInstance::with_externvals(
|
||||
&module_with_single_import,
|
||||
[
|
||||
ExternVal::Func(FuncInstance::alloc_host(
|
||||
[ExternVal::Func(FuncInstance::alloc_host(
|
||||
Signature::new(&[][..], Some(ValueType::I32)),
|
||||
0
|
||||
),)
|
||||
].iter(),
|
||||
).is_err()
|
||||
);
|
||||
),)]
|
||||
.iter(),
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
#[cfg(not(feature = "std"))]
|
||||
use libm::{F32Ext, F64Ext};
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub, Rem};
|
||||
use core::cmp::{Ordering, PartialEq, PartialOrd};
|
||||
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
|
||||
|
||||
macro_rules! impl_binop {
|
||||
($for:ident, $is:ident, $op:ident, $func_name:ident) => {
|
||||
|
@ -13,19 +13,22 @@ macro_rules! impl_binop {
|
|||
|
||||
fn $func_name(self, other: T) -> Self {
|
||||
$for(
|
||||
$op::$func_name(
|
||||
$is::from_bits(self.0),
|
||||
$is::from_bits(other.into().0)
|
||||
).to_bits()
|
||||
$op::$func_name($is::from_bits(self.0), $is::from_bits(other.into().0))
|
||||
.to_bits(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! float {
|
||||
($for:ident, $rep:ident, $is:ident) => {
|
||||
float!($for, $rep, $is, 1 << (::core::mem::size_of::<$is>() * 8 - 1));
|
||||
float!(
|
||||
$for,
|
||||
$rep,
|
||||
$is,
|
||||
1 << (::core::mem::size_of::<$is>() * 8 - 1)
|
||||
);
|
||||
};
|
||||
($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => {
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -112,7 +115,7 @@ macro_rules! float {
|
|||
$is::from(*self).fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
float!(F32, u32, f32);
|
||||
|
@ -150,9 +153,9 @@ mod tests {
|
|||
|
||||
use super::{F32, F64};
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::fmt::Debug;
|
||||
use core::iter;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
|
||||
fn test_ops<T, F, I>(iter: I)
|
||||
where
|
||||
|
|
|
@ -1347,7 +1347,8 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu
|
|||
.any(|(expected_type, param_value)| {
|
||||
let actual_type = param_value.value_type();
|
||||
&actual_type != expected_type
|
||||
}) {
|
||||
})
|
||||
{
|
||||
return Err(TrapKind::UnexpectedSignature.into());
|
||||
}
|
||||
|
||||
|
|
32
src/table.rs
32
src/table.rs
|
@ -1,13 +1,13 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::u32;
|
||||
use core::fmt;
|
||||
use core::cell::RefCell;
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
use core::fmt;
|
||||
use core::u32;
|
||||
use func::FuncRef;
|
||||
use module::check_limits;
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
|
||||
/// Reference to a table (See [`TableInstance`] for details).
|
||||
///
|
||||
|
@ -106,7 +106,9 @@ impl TableInstance {
|
|||
pub fn grow(&self, by: u32) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let maximum_size = self.maximum_size().unwrap_or(u32::MAX);
|
||||
let new_size = self.current_size().checked_add(by)
|
||||
let new_size = self
|
||||
.current_size()
|
||||
.checked_add(by)
|
||||
.and_then(|new_size| {
|
||||
if maximum_size < new_size {
|
||||
None
|
||||
|
@ -114,13 +116,13 @@ impl TableInstance {
|
|||
Some(new_size)
|
||||
}
|
||||
})
|
||||
.ok_or_else(||
|
||||
.ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"Trying to grow table by {} items when there are already {} items",
|
||||
by,
|
||||
self.current_size(),
|
||||
))
|
||||
)?;
|
||||
})?;
|
||||
buffer.resize(new_size as usize, None);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -129,13 +131,12 @@ impl TableInstance {
|
|||
pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> {
|
||||
let buffer = self.buffer.borrow();
|
||||
let buffer_len = buffer.len();
|
||||
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(||
|
||||
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"trying to read table item with index {} when there are only {} items",
|
||||
offset,
|
||||
buffer_len
|
||||
)),
|
||||
)?;
|
||||
offset, buffer_len
|
||||
))
|
||||
})?;
|
||||
Ok(table_elem)
|
||||
}
|
||||
|
||||
|
@ -143,13 +144,12 @@ impl TableInstance {
|
|||
pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let buffer_len = buffer.len();
|
||||
let table_elem = buffer.get_mut(offset as usize).ok_or_else(||
|
||||
let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"trying to update table item with index {} when there are only {} items",
|
||||
offset,
|
||||
buffer_len
|
||||
offset, buffer_len
|
||||
))
|
||||
)?;
|
||||
})?;
|
||||
*table_elem = value;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use {
|
||||
Error, Signature, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
|
||||
MemoryInstance, MemoryRef, TableInstance, TableRef, ModuleImportResolver, ModuleInstance, ModuleRef,
|
||||
RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, ResumableError,
|
||||
};
|
||||
use types::ValueType;
|
||||
use memory_units::Pages;
|
||||
use super::parse_wat;
|
||||
use memory_units::Pages;
|
||||
use types::ValueType;
|
||||
use {
|
||||
Error, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, ResumableError,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap, TrapKind,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct HostErrorWithCode {
|
||||
|
@ -107,9 +107,10 @@ impl Externals for TestHost {
|
|||
INC_MEM_FUNC_INDEX => {
|
||||
let ptr: u32 = args.nth(0);
|
||||
|
||||
let memory = self.memory.as_ref().expect(
|
||||
"Function 'inc_mem' expects attached memory",
|
||||
);
|
||||
let memory = self
|
||||
.memory
|
||||
.as_ref()
|
||||
.expect("Function 'inc_mem' expects attached memory");
|
||||
let mut buf = [0u8; 1];
|
||||
memory.get_into(ptr, &mut buf).unwrap();
|
||||
buf[0] += 1;
|
||||
|
@ -120,18 +121,22 @@ impl Externals for TestHost {
|
|||
GET_MEM_FUNC_INDEX => {
|
||||
let ptr: u32 = args.nth(0);
|
||||
|
||||
let memory = self.memory.as_ref().expect(
|
||||
"Function 'get_mem' expects attached memory",
|
||||
);
|
||||
let memory = self
|
||||
.memory
|
||||
.as_ref()
|
||||
.expect("Function 'get_mem' expects attached memory");
|
||||
let mut buf = [0u8; 1];
|
||||
memory.get_into(ptr, &mut buf).unwrap();
|
||||
|
||||
Ok(Some(RuntimeValue::I32(buf[0] as i32)))
|
||||
}
|
||||
RECURSE_FUNC_INDEX => {
|
||||
let val = args.nth_value_checked(0).expect("Exactly one argument expected");
|
||||
let val = args
|
||||
.nth_value_checked(0)
|
||||
.expect("Exactly one argument expected");
|
||||
|
||||
let instance = self.instance
|
||||
let instance = self
|
||||
.instance
|
||||
.as_ref()
|
||||
.expect("Function 'recurse' expects attached module instance")
|
||||
.clone();
|
||||
|
@ -141,7 +146,9 @@ impl Externals for TestHost {
|
|||
.expect("expected to be Some");
|
||||
|
||||
if val.value_type() != result.value_type() {
|
||||
return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into());
|
||||
return Err(
|
||||
TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into(),
|
||||
);
|
||||
}
|
||||
Ok(Some(result))
|
||||
}
|
||||
|
@ -192,17 +199,17 @@ impl ModuleImportResolver for TestHost {
|
|||
"recurse" => RECURSE_FUNC_INDEX,
|
||||
"trap_sub" => TRAP_SUB_FUNC_INDEX,
|
||||
_ => {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
if !self.check_signature(index, signature) {
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export `{}` doesnt match expected type {:?}",
|
||||
field_name,
|
||||
signature
|
||||
field_name, signature
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -214,9 +221,10 @@ impl ModuleImportResolver for TestHost {
|
|||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,9 +252,9 @@ fn call_host_func() {
|
|||
.assert_no_start();
|
||||
|
||||
assert_eq!(
|
||||
instance.invoke_export("test", &[], &mut env).expect(
|
||||
"Failed to invoke 'test' function",
|
||||
),
|
||||
instance
|
||||
.invoke_export("test", &[], &mut env)
|
||||
.expect("Failed to invoke 'test' function",),
|
||||
Some(RuntimeValue::I32(-2))
|
||||
);
|
||||
}
|
||||
|
@ -280,16 +288,16 @@ fn resume_call_host_func() {
|
|||
let mut invocation = FuncInstance::invoke_resumable(&func_instance, &[]).unwrap();
|
||||
let result = invocation.start_execution(&mut env);
|
||||
match result {
|
||||
Err(ResumableError::Trap(_)) => {},
|
||||
Err(ResumableError::Trap(_)) => {}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
assert!(invocation.is_resumable());
|
||||
let trap_sub_result = env.trap_sub_result.take();
|
||||
assert_eq!(
|
||||
invocation.resume_execution(trap_sub_result, &mut env).expect(
|
||||
"Failed to invoke 'test' function",
|
||||
),
|
||||
invocation
|
||||
.resume_execution(trap_sub_result, &mut env)
|
||||
.expect("Failed to invoke 'test' function",),
|
||||
Some(RuntimeValue::I32(-2))
|
||||
);
|
||||
}
|
||||
|
@ -316,13 +324,15 @@ fn host_err() {
|
|||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let error = instance.invoke_export("test", &[], &mut env).expect_err(
|
||||
"`test` expected to return error",
|
||||
);
|
||||
let error = instance
|
||||
.invoke_export("test", &[], &mut env)
|
||||
.expect_err("`test` expected to return error");
|
||||
|
||||
let error_with_code = error.as_host_error().expect("Expected host error").downcast_ref::<HostErrorWithCode>().expect(
|
||||
"Failed to downcast to expected error type",
|
||||
);
|
||||
let error_with_code = error
|
||||
.as_host_error()
|
||||
.expect("Expected host error")
|
||||
.downcast_ref::<HostErrorWithCode>()
|
||||
.expect("Failed to downcast to expected error type");
|
||||
assert_eq!(error_with_code.error_code, 228);
|
||||
}
|
||||
|
||||
|
@ -351,9 +361,9 @@ fn modify_mem_with_host_funcs() {
|
|||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
instance.invoke_export("modify_mem", &[], &mut env).expect(
|
||||
"Failed to invoke 'test' function",
|
||||
);
|
||||
instance
|
||||
.invoke_export("modify_mem", &[], &mut env)
|
||||
.expect("Failed to invoke 'test' function");
|
||||
|
||||
// Check contents of memory at address 12.
|
||||
let mut buf = [0u8; 1];
|
||||
|
@ -451,9 +461,9 @@ fn recursion() {
|
|||
env.instance = Some(instance.clone());
|
||||
|
||||
assert_eq!(
|
||||
instance.invoke_export("test", &[], &mut env).expect(
|
||||
"Failed to invoke 'test' function",
|
||||
),
|
||||
instance
|
||||
.invoke_export("test", &[], &mut env)
|
||||
.expect("Failed to invoke 'test' function",),
|
||||
// 363 = 321 + 42
|
||||
Some(RuntimeValue::I64(363))
|
||||
);
|
||||
|
@ -472,21 +482,17 @@ fn defer_providing_externals() {
|
|||
}
|
||||
|
||||
impl ModuleImportResolver for HostImportResolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
if field_name != "inc" {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
));
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
if signature.params() != &[ValueType::I32] || signature.return_type() != None {
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export `{}` doesnt match expected type {:?}",
|
||||
field_name,
|
||||
signature
|
||||
field_name, signature
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -501,9 +507,10 @@ fn defer_providing_externals() {
|
|||
if field_name == "mem" {
|
||||
Ok(self.mem.clone())
|
||||
} else {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -547,14 +554,16 @@ fn defer_providing_externals() {
|
|||
|
||||
// Create HostImportResolver with some initialized memory instance.
|
||||
// This memory instance will be provided as 'mem' export.
|
||||
let host_import_resolver =
|
||||
HostImportResolver { mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap() };
|
||||
let host_import_resolver = HostImportResolver {
|
||||
mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap(),
|
||||
};
|
||||
|
||||
// Instantiate module with `host_import_resolver` as import resolver for "host" module.
|
||||
let instance = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::new().with_resolver("host", &host_import_resolver),
|
||||
).expect("Failed to instantiate module")
|
||||
)
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let mut acc = 89;
|
||||
|
@ -599,18 +608,15 @@ fn two_envs_one_externals() {
|
|||
struct OrdinaryResolver;
|
||||
|
||||
impl ModuleImportResolver for PrivilegedResolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
let index = match field_name {
|
||||
"ordinary" => ORDINARY_FUNC_INDEX,
|
||||
"privileged" => PRIVILEGED_FUNC_INDEX,
|
||||
_ => {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -619,22 +625,19 @@ fn two_envs_one_externals() {
|
|||
}
|
||||
|
||||
impl ModuleImportResolver for OrdinaryResolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
let index = match field_name {
|
||||
"ordinary" => ORDINARY_FUNC_INDEX,
|
||||
"privileged" => {
|
||||
return Err(Error::Instantiation(
|
||||
"'priveleged' can be imported only in privileged context".into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -673,7 +676,8 @@ fn two_envs_one_externals() {
|
|||
let trusted_instance = ModuleInstance::new(
|
||||
&trusted_module,
|
||||
&ImportsBuilder::new().with_resolver("env", &PrivilegedResolver),
|
||||
).expect("Failed to instantiate module")
|
||||
)
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let untrusted_instance = ModuleInstance::new(
|
||||
|
@ -681,7 +685,8 @@ fn two_envs_one_externals() {
|
|||
&ImportsBuilder::new()
|
||||
.with_resolver("env", &OrdinaryResolver)
|
||||
.with_resolver("trusted", &trusted_instance),
|
||||
).expect("Failed to instantiate module")
|
||||
)
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
untrusted_instance
|
||||
|
@ -716,7 +721,8 @@ fn dynamically_add_host_func() {
|
|||
Signature::new(&[][..], Some(ValueType::I32)),
|
||||
host_func_index as usize,
|
||||
);
|
||||
self.table.set(table_index, Some(added_func))
|
||||
self.table
|
||||
.set(table_index, Some(added_func))
|
||||
.map_err(|_| TrapKind::TableAccessOutOfBounds)?;
|
||||
|
||||
Ok(Some(RuntimeValue::I32(table_index as i32)))
|
||||
|
@ -730,17 +736,14 @@ fn dynamically_add_host_func() {
|
|||
}
|
||||
|
||||
impl ModuleImportResolver for HostExternals {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
let index = match field_name {
|
||||
"add_func" => ADD_FUNC_FUNC_INDEX,
|
||||
_ => {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
};
|
||||
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
||||
|
@ -754,9 +757,10 @@ fn dynamically_add_host_func() {
|
|||
if field_name == "table" {
|
||||
Ok(self.table.clone())
|
||||
} else {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -788,7 +792,8 @@ fn dynamically_add_host_func() {
|
|||
let instance = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::new().with_resolver("env", &host_externals),
|
||||
).expect("Failed to instantiate module")
|
||||
)
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
assert_eq!(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
@ -116,15 +119,12 @@ fn interpreter_accumulate_u8() {
|
|||
// 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
|
||||
let module = load_from_file(WASM_FILE);
|
||||
|
||||
let env = Env::new();
|
||||
let instance = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::new().with_resolver("env", &env),
|
||||
).expect("Failed to instantiate module")
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let env_memory = env.memory.clone();
|
||||
|
@ -134,7 +134,10 @@ fn interpreter_accumulate_u8() {
|
|||
let _ = env_memory.set(offset, BUF);
|
||||
|
||||
// Set up the function argument list and invoke the function
|
||||
let args = &[RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)];
|
||||
let args = &[
|
||||
RuntimeValue::I32(BUF.len() as i32),
|
||||
RuntimeValue::I32(offset as i32),
|
||||
];
|
||||
let retval = instance
|
||||
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
|
||||
.expect("Failed to execute function");
|
||||
|
|
12
src/types.rs
12
src/types.rs
|
@ -1,7 +1,8 @@
|
|||
use alloc::borrow::Cow;
|
||||
|
||||
use parity_wasm::elements::{
|
||||
FunctionType, ValueType as EValueType, GlobalType, TableType, MemoryType};
|
||||
FunctionType, GlobalType, MemoryType, TableType, ValueType as EValueType,
|
||||
};
|
||||
|
||||
/// Signature of a [function].
|
||||
///
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)))?;
|
||||
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
use alloc::prelude::*;
|
||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||
use core::u32;
|
||||
use parity_wasm::elements::{
|
||||
BlockType, Func, FuncBody, Instruction, TableElementType, ValueType,
|
||||
};
|
||||
use parity_wasm::elements::{BlockType, Func, FuncBody, Instruction, TableElementType, ValueType};
|
||||
use validation::context::ModuleContext;
|
||||
|
||||
use validation::util::Locals;
|
||||
|
@ -1956,4 +1954,3 @@ impl Sink {
|
|||
self.ins
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use core::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashSet;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet;
|
||||
|
||||
use parity_wasm::elements::{
|
||||
BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction,
|
||||
ResizableLimits, TableType, ValueType, InitExpr, Type,
|
||||
};
|
||||
use common::stack;
|
||||
use self::context::ModuleContextBuilder;
|
||||
use self::func::FunctionReader;
|
||||
use memory_units::Pages;
|
||||
use common::stack;
|
||||
use isa;
|
||||
use memory_units::Pages;
|
||||
use parity_wasm::elements::{
|
||||
BlockType, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, MemoryType,
|
||||
Module, ResizableLimits, TableType, Type, ValueType,
|
||||
};
|
||||
|
||||
mod context;
|
||||
mod func;
|
||||
|
@ -158,7 +158,8 @@ pub fn deny_floating_point(module: &Module) -> Result<(), Error> {
|
|||
if let Some(typ) = types.get(sig.type_ref() as usize) {
|
||||
match *typ {
|
||||
Type::Function(ref func) => {
|
||||
if func.params()
|
||||
if func
|
||||
.params()
|
||||
.iter()
|
||||
.chain(func.return_type().as_ref())
|
||||
.any(|&typ| typ == ValueType::F32 || typ == ValueType::F64)
|
||||
|
@ -245,32 +246,32 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
|||
if function_section_len != code_section_len {
|
||||
return Err(Error(format!(
|
||||
"length of function section is {}, while len of code section is {}",
|
||||
function_section_len,
|
||||
code_section_len
|
||||
function_section_len, code_section_len
|
||||
)));
|
||||
}
|
||||
|
||||
// validate every function body in user modules
|
||||
if function_section_len != 0 {
|
||||
// tests use invalid code
|
||||
let function_section = module.function_section().expect(
|
||||
"function_section_len != 0; qed",
|
||||
);
|
||||
let code_section = module.code_section().expect(
|
||||
"function_section_len != 0; function_section_len == code_section_len; qed",
|
||||
);
|
||||
let function_section = module
|
||||
.function_section()
|
||||
.expect("function_section_len != 0; qed");
|
||||
let code_section = module
|
||||
.code_section()
|
||||
.expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
||||
// check every function body
|
||||
for (index, function) in function_section.entries().iter().enumerate() {
|
||||
let function_body = code_section.bodies().get(index as usize).ok_or(
|
||||
Error(format!(
|
||||
"Missing body for function {}",
|
||||
index
|
||||
)),
|
||||
)?;
|
||||
let code = FunctionReader::read_function(&context, function, function_body)
|
||||
.map_err(|e| {
|
||||
let function_body = code_section
|
||||
.bodies()
|
||||
.get(index as usize)
|
||||
.ok_or(Error(format!("Missing body for function {}", index)))?;
|
||||
let code =
|
||||
FunctionReader::read_function(&context, function, function_body).map_err(|e| {
|
||||
let Error(ref msg) = e;
|
||||
Error(format!("Function #{} reading/validation error: {}", index, msg))
|
||||
Error(format!(
|
||||
"Function #{} reading/validation error: {}",
|
||||
index, msg
|
||||
))
|
||||
})?;
|
||||
code_map.push(code);
|
||||
}
|
||||
|
@ -293,9 +294,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
|||
// HashSet::insert returns false if item already in set.
|
||||
let duplicate = export_names.insert(export.field()) == false;
|
||||
if duplicate {
|
||||
return Err(Error(
|
||||
format!("duplicate export {}", export.field()),
|
||||
));
|
||||
return Err(Error(format!("duplicate export {}", export.field())));
|
||||
}
|
||||
match *export.internal() {
|
||||
Internal::Function(function_index) => {
|
||||
|
@ -382,10 +381,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(ValidatedModule {
|
||||
module,
|
||||
code_map,
|
||||
})
|
||||
Ok(ValidatedModule { module, code_map })
|
||||
}
|
||||
|
||||
fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
||||
|
@ -437,8 +433,7 @@ fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<Value
|
|||
Instruction::I64Const(_) => ValueType::I64,
|
||||
Instruction::F32Const(_) => ValueType::F32,
|
||||
Instruction::F64Const(_) => ValueType::F64,
|
||||
Instruction::GetGlobal(idx) => {
|
||||
match globals.get(idx as usize) {
|
||||
Instruction::GetGlobal(idx) => match globals.get(idx as usize) {
|
||||
Some(target_global) => {
|
||||
if target_global.is_mutable() {
|
||||
return Err(Error(format!("Global {} is mutable", idx)));
|
||||
|
@ -446,12 +441,12 @@ fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<Value
|
|||
target_global.content_type()
|
||||
}
|
||||
None => {
|
||||
return Err(Error(
|
||||
format!("Global {} doesn't exists or not yet defined", idx),
|
||||
))
|
||||
}
|
||||
}
|
||||
return Err(Error(format!(
|
||||
"Global {} doesn't exists or not yet defined",
|
||||
idx
|
||||
)));
|
||||
}
|
||||
},
|
||||
_ => return Err(Error("Non constant opcode in init expr".into())),
|
||||
};
|
||||
if code[1] != Instruction::End {
|
||||
|
|
|
@ -35,7 +35,8 @@ fn limits() {
|
|||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(min, max)),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
|
||||
// defined memory
|
||||
|
@ -53,7 +54,8 @@ fn limits() {
|
|||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(min, max)),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +66,8 @@ fn global_init_const() {
|
|||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// init expr type differs from declared global type
|
||||
|
@ -72,7 +75,8 @@ fn global_init_const() {
|
|||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I64, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -83,10 +87,12 @@ fn global_init_global() {
|
|||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||
)).with_global(GlobalEntry::new(
|
||||
))
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// get_global can reference only previously defined globals
|
||||
|
@ -94,7 +100,8 @@ fn global_init_global() {
|
|||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// get_global can reference only const globals
|
||||
|
@ -103,10 +110,12 @@ fn global_init_global() {
|
|||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||
)).with_global(GlobalEntry::new(
|
||||
))
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// get_global in init_expr can only refer to imported globals.
|
||||
|
@ -114,10 +123,12 @@ fn global_init_global() {
|
|||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, false),
|
||||
InitExpr::new(vec![Instruction::I32Const(0), Instruction::End]),
|
||||
)).with_global(GlobalEntry::new(
|
||||
))
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -128,7 +139,8 @@ fn global_init_misc() {
|
|||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42)]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// empty init expr
|
||||
|
@ -136,7 +148,8 @@ fn global_init_misc() {
|
|||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::End]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// not an constant opcode used
|
||||
|
@ -144,7 +157,8 @@ fn global_init_misc() {
|
|||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::Unreachable, Instruction::End]),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -156,7 +170,8 @@ fn module_limits_validity() {
|
|||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(10, None)),
|
||||
)).memory()
|
||||
))
|
||||
.memory()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.build();
|
||||
|
@ -168,7 +183,8 @@ fn module_limits_validity() {
|
|||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(10, None)),
|
||||
)).table()
|
||||
))
|
||||
.table()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.build();
|
||||
|
@ -188,7 +204,8 @@ fn funcs() {
|
|||
.with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(1),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
]))
|
||||
.build()
|
||||
.build()
|
||||
.function()
|
||||
.signature()
|
||||
|
@ -199,7 +216,8 @@ fn funcs() {
|
|||
.with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(0),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
]))
|
||||
.build()
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
@ -213,7 +231,8 @@ fn globals() {
|
|||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// import mutable global is invalid.
|
||||
|
@ -222,7 +241,8 @@ fn globals() {
|
|||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||
)).build();
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -245,7 +265,8 @@ fn if_else_with_return_type_validation() {
|
|||
Instruction::Drop,
|
||||
Instruction::End,
|
||||
Instruction::End,
|
||||
])).build()
|
||||
]))
|
||||
.build()
|
||||
.build()
|
||||
.build();
|
||||
validate_module(m).unwrap();
|
||||
|
|
|
@ -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(_));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
mod run;
|
||||
|
||||
macro_rules! run_test {
|
||||
($label: expr, $test_name: ident) => (
|
||||
($label: expr, $test_name: ident) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
self::run::spec($label)
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
run_test!("address", wasm_address);
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
|
||||
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
|
||||
use wasmi::memory_units::Pages;
|
||||
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
|
||||
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap};
|
||||
use wasmi::{
|
||||
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance,
|
||||
GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
|
||||
ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature,
|
||||
TableDescriptor, TableInstance, TableRef, Trap,
|
||||
};
|
||||
|
||||
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
|
||||
match val {
|
||||
|
@ -190,7 +192,8 @@ impl SpecDriver {
|
|||
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
|
||||
match name {
|
||||
Some(name) => self.module(name),
|
||||
None => self.last_module
|
||||
None => self
|
||||
.last_module
|
||||
.clone()
|
||||
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
|
||||
}
|
||||
|
@ -301,7 +304,8 @@ fn run_action(
|
|||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let vec_args = args.iter()
|
||||
let vec_args = args
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -345,9 +349,12 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
|||
use std::io::Read;
|
||||
let mut spec_source = Vec::new();
|
||||
let mut spec_file = File::open(&spec_script_path).expect("Can't open file");
|
||||
spec_file.read_to_end(&mut spec_source).expect("Can't read file");
|
||||
spec_file
|
||||
.read_to_end(&mut spec_source)
|
||||
.expect("Can't read file");
|
||||
|
||||
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)).expect("Can't read spec script");
|
||||
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name))
|
||||
.expect("Can't read spec script");
|
||||
let mut errors = vec![];
|
||||
|
||||
while let Some(Command { kind, line }) = parser.next()? {
|
||||
|
@ -414,12 +421,16 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
|||
Ok(result) => {
|
||||
for actual_result in result.into_iter().collect::<Vec<RuntimeValue>>() {
|
||||
match actual_result {
|
||||
RuntimeValue::F32(val) => if !val.is_nan() {
|
||||
RuntimeValue::F32(val) => {
|
||||
if !val.is_nan() {
|
||||
panic!("Expected nan value, got {:?}", val)
|
||||
},
|
||||
RuntimeValue::F64(val) => if !val.is_nan() {
|
||||
}
|
||||
}
|
||||
RuntimeValue::F64(val) => {
|
||||
if !val.is_nan() {
|
||||
panic!("Expected nan value, got {:?}", val)
|
||||
},
|
||||
}
|
||||
}
|
||||
val @ _ => {
|
||||
panic!("Expected action to return float value, got {:?}", val)
|
||||
}
|
||||
|
@ -435,7 +446,7 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
|||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
|
||||
Err(_e) => {},
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertTrap { action, .. } => {
|
||||
|
@ -456,13 +467,13 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
|||
let module_load = try_load(&module.into_vec(), &mut spec_driver);
|
||||
match module_load {
|
||||
Ok(_) => panic!("Expected invalid module definition, got some module!"),
|
||||
Err(_e) => {},
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertUninstantiable { module, .. } => {
|
||||
match try_load(&module.into_vec(), &mut spec_driver) {
|
||||
Ok(_) => panic!("Expected error running start function at line {}", line),
|
||||
Err(_e) => {},
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::Register { name, as_name, .. } => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Official spec testsuite.
|
||||
|
||||
extern crate wasmi;
|
||||
extern crate wabt;
|
||||
extern crate wasmi;
|
||||
|
||||
mod spec;
|
||||
|
|
Loading…
Reference in New Issue