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