run 'cargo fmt' again
This commit is contained in:
parent
d5b99afe0b
commit
2824b84381
|
@ -22,9 +22,7 @@ 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
|
let function_section = module.function_section().expect("No function section found");
|
||||||
.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");
|
||||||
|
|
||||||
|
@ -59,8 +57,7 @@ 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 =
|
let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as 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] {
|
||||||
|
@ -112,7 +109,6 @@ fn main() {
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Result: {:?}",
|
"Result: {:?}",
|
||||||
main.invoke_export(func_name, &args, &mut NopExternals)
|
main.invoke_export(func_name, &args, &mut NopExternals).expect("")
|
||||||
.expect("")
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,8 @@ use std::env;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use wasmi::{
|
use wasmi::{
|
||||||
Error as InterpreterError, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
|
Error as InterpreterError, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, ModuleImportResolver,
|
||||||
ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, Trap,
|
ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, Trap, ValueType,
|
||||||
ValueType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -140,11 +139,7 @@ const SET_FUNC_INDEX: usize = 0;
|
||||||
const GET_FUNC_INDEX: usize = 1;
|
const GET_FUNC_INDEX: usize = 1;
|
||||||
|
|
||||||
impl<'a> Externals for Runtime<'a> {
|
impl<'a> Externals for Runtime<'a> {
|
||||||
fn invoke_index(
|
fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
&mut self,
|
|
||||||
index: usize,
|
|
||||||
args: RuntimeArgs,
|
|
||||||
) -> Result<Option<RuntimeValue>, Trap> {
|
|
||||||
match index {
|
match index {
|
||||||
SET_FUNC_INDEX => {
|
SET_FUNC_INDEX => {
|
||||||
let idx: i32 = args.nth(0);
|
let idx: i32 = args.nth(0);
|
||||||
|
@ -164,16 +159,9 @@ impl<'a> Externals for Runtime<'a> {
|
||||||
struct RuntimeModuleImportResolver;
|
struct RuntimeModuleImportResolver;
|
||||||
|
|
||||||
impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
|
impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
|
||||||
fn resolve_func(
|
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, InterpreterError> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_signature: &Signature,
|
|
||||||
) -> Result<FuncRef, InterpreterError> {
|
|
||||||
let func_ref = match field_name {
|
let func_ref = match field_name {
|
||||||
"set" => FuncInstance::alloc_host(
|
"set" => FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], None), SET_FUNC_INDEX),
|
||||||
Signature::new(&[ValueType::I32][..], None),
|
|
||||||
SET_FUNC_INDEX,
|
|
||||||
),
|
|
||||||
"get" => FuncInstance::alloc_host(
|
"get" => FuncInstance::alloc_host(
|
||||||
Signature::new(&[ValueType::I32][..], Some(ValueType::I32)),
|
Signature::new(&[ValueType::I32][..], Some(ValueType::I32)),
|
||||||
GET_FUNC_INDEX,
|
GET_FUNC_INDEX,
|
||||||
|
|
|
@ -7,9 +7,9 @@ use std::env::args;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use wasmi::memory_units::*;
|
use wasmi::memory_units::*;
|
||||||
use wasmi::{
|
use wasmi::{
|
||||||
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder,
|
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor,
|
||||||
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance,
|
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, Signature,
|
||||||
NopExternals, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
|
TableDescriptor, TableInstance, TableRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn load_from_file(filename: &str) -> Module {
|
fn load_from_file(filename: &str) -> Module {
|
||||||
|
@ -27,33 +27,21 @@ impl ModuleImportResolver for ResolveAll {
|
||||||
Ok(FuncInstance::alloc_host(signature.clone(), 0))
|
Ok(FuncInstance::alloc_host(signature.clone(), 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_global(
|
fn resolve_global(&self, _field_name: &str, global_type: &GlobalDescriptor) -> Result<GlobalRef, Error> {
|
||||||
&self,
|
|
||||||
_field_name: &str,
|
|
||||||
global_type: &GlobalDescriptor,
|
|
||||||
) -> Result<GlobalRef, Error> {
|
|
||||||
Ok(GlobalInstance::alloc(
|
Ok(GlobalInstance::alloc(
|
||||||
RuntimeValue::default(global_type.value_type()),
|
RuntimeValue::default(global_type.value_type()),
|
||||||
global_type.is_mutable(),
|
global_type.is_mutable(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_memory(
|
fn resolve_memory(&self, _field_name: &str, memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
|
||||||
&self,
|
|
||||||
_field_name: &str,
|
|
||||||
memory_type: &MemoryDescriptor,
|
|
||||||
) -> Result<MemoryRef, Error> {
|
|
||||||
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(&self, _field_name: &str, table_type: &TableDescriptor) -> Result<TableRef, Error> {
|
||||||
&self,
|
|
||||||
_field_name: &str,
|
|
||||||
table_type: &TableDescriptor,
|
|
||||||
) -> Result<TableRef, Error> {
|
|
||||||
Ok(TableInstance::alloc(table_type.initial(), table_type.maximum()).unwrap())
|
Ok(TableInstance::alloc(table_type.initial(), table_type.maximum()).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
21
src/func.rs
21
src/func.rs
|
@ -63,9 +63,7 @@ impl fmt::Debug for FuncInstance {
|
||||||
// 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!(f, "Internal {{ signature={:?} }}", signature,)
|
write!(f, "Internal {{ signature={:?} }}", signature,)
|
||||||
}
|
}
|
||||||
&FuncInstanceInternal::Host { ref signature, .. } => {
|
&FuncInstanceInternal::Host { ref signature, .. } => write!(f, "Host {{ signature={:?} }}", signature),
|
||||||
write!(f, "Host {{ signature={:?} }}", signature)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,11 +101,7 @@ impl FuncInstance {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn alloc_internal(
|
pub(crate) fn alloc_internal(module: Weak<ModuleInstance>, signature: Rc<Signature>, body: FuncBody) -> FuncRef {
|
||||||
module: Weak<ModuleInstance>,
|
|
||||||
signature: Rc<Signature>,
|
|
||||||
body: FuncBody,
|
|
||||||
) -> FuncRef {
|
|
||||||
let func = FuncInstanceInternal::Internal {
|
let func = FuncInstanceInternal::Internal {
|
||||||
signature,
|
signature,
|
||||||
module: module,
|
module: module,
|
||||||
|
@ -144,8 +138,7 @@ impl FuncInstance {
|
||||||
interpreter.start_execution(externals)
|
interpreter.start_execution(externals)
|
||||||
}
|
}
|
||||||
FuncInstanceInternal::Host {
|
FuncInstanceInternal::Host {
|
||||||
ref host_func_index,
|
ref host_func_index, ..
|
||||||
..
|
|
||||||
} => externals.invoke_index(*host_func_index, args.into()),
|
} => externals.invoke_index(*host_func_index, args.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,10 +157,7 @@ impl FuncInstance {
|
||||||
/// [`Trap`]: #enum.Trap.html
|
/// [`Trap`]: #enum.Trap.html
|
||||||
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
|
/// [`start_execution`]: struct.FuncInvocation.html#method.start_execution
|
||||||
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
|
/// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution
|
||||||
pub fn invoke_resumable<'args>(
|
pub fn invoke_resumable<'args>(func: &FuncRef, args: &'args [RuntimeValue]) -> Result<FuncInvocation<'args>, Trap> {
|
||||||
func: &FuncRef,
|
|
||||||
args: &'args [RuntimeValue],
|
|
||||||
) -> Result<FuncInvocation<'args>, Trap> {
|
|
||||||
check_function_args(func.signature(), &args)?;
|
check_function_args(func.signature(), &args)?;
|
||||||
match *func.as_internal() {
|
match *func.as_internal() {
|
||||||
FuncInstanceInternal::Internal { .. } => {
|
FuncInstanceInternal::Internal { .. } => {
|
||||||
|
@ -177,8 +167,7 @@ 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,
|
||||||
|
|
|
@ -57,9 +57,7 @@ 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(
|
return Err(Error::Global("Attempt to change an immutable variable".into()));
|
||||||
"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()));
|
||||||
|
|
12
src/host.rs
12
src/host.rs
|
@ -208,11 +208,7 @@ impl HostError {
|
||||||
/// ```
|
/// ```
|
||||||
pub trait Externals {
|
pub trait Externals {
|
||||||
/// Perform invoke of a host function by specified `index`.
|
/// Perform invoke of a host function by specified `index`.
|
||||||
fn invoke_index(
|
fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap>;
|
||||||
&mut self,
|
|
||||||
index: usize,
|
|
||||||
args: RuntimeArgs,
|
|
||||||
) -> Result<Option<RuntimeValue>, Trap>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of [`Externals`] that just traps on [`invoke_index`].
|
/// Implementation of [`Externals`] that just traps on [`invoke_index`].
|
||||||
|
@ -222,11 +218,7 @@ pub trait Externals {
|
||||||
pub struct NopExternals;
|
pub struct NopExternals;
|
||||||
|
|
||||||
impl Externals for NopExternals {
|
impl Externals for NopExternals {
|
||||||
fn invoke_index(
|
fn invoke_index(&mut self, _index: usize, _args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
&mut self,
|
|
||||||
_index: usize,
|
|
||||||
_args: RuntimeArgs,
|
|
||||||
) -> Result<Option<RuntimeValue>, Trap> {
|
|
||||||
Err(TrapKind::Unreachable.into())
|
Err(TrapKind::Unreachable.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,7 @@ pub trait ImportResolver {
|
||||||
///
|
///
|
||||||
/// 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.
|
||||||
/// Otherwise, link-time error will occur.
|
/// Otherwise, link-time error will occur.
|
||||||
fn resolve_func(
|
fn resolve_func(&self, _module_name: &str, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error>;
|
||||||
&self,
|
|
||||||
_module_name: &str,
|
|
||||||
field_name: &str,
|
|
||||||
_signature: &Signature,
|
|
||||||
) -> Result<FuncRef, Error>;
|
|
||||||
|
|
||||||
/// Resolve a global variable.
|
/// Resolve a global variable.
|
||||||
///
|
///
|
||||||
|
@ -124,11 +119,7 @@ impl<'a> ImportsBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register an resolver by a name.
|
/// Register an resolver by a name.
|
||||||
pub fn with_resolver<N: Into<String>>(
|
pub fn with_resolver<N: Into<String>>(mut self, name: N, resolver: &'a ModuleImportResolver) -> Self {
|
||||||
mut self,
|
|
||||||
name: N,
|
|
||||||
resolver: &'a ModuleImportResolver,
|
|
||||||
) -> Self {
|
|
||||||
self.modules.insert(name.into(), resolver);
|
self.modules.insert(name.into(), resolver);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -146,12 +137,7 @@ impl<'a> ImportsBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ImportResolver for ImportsBuilder<'a> {
|
impl<'a> ImportResolver for ImportsBuilder<'a> {
|
||||||
fn resolve_func(
|
fn resolve_func(&self, module_name: &str, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||||
&self,
|
|
||||||
module_name: &str,
|
|
||||||
field_name: &str,
|
|
||||||
signature: &Signature,
|
|
||||||
) -> Result<FuncRef, Error> {
|
|
||||||
self.resolver(module_name)
|
self.resolver(module_name)
|
||||||
.ok_or_else(|| 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)
|
||||||
|
@ -201,10 +187,7 @@ pub trait ModuleImportResolver {
|
||||||
///
|
///
|
||||||
/// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
|
/// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
|
||||||
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
|
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
|
||||||
Err(Error::Instantiation(format!(
|
Err(Error::Instantiation(format!("Export {} not found", field_name)))
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a global variable.
|
/// Resolve a global variable.
|
||||||
|
@ -212,15 +195,8 @@ pub trait ModuleImportResolver {
|
||||||
/// See [`ImportResolver::resolve_global`] for details.
|
/// See [`ImportResolver::resolve_global`] for details.
|
||||||
///
|
///
|
||||||
/// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global
|
/// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global
|
||||||
fn resolve_global(
|
fn resolve_global(&self, field_name: &str, _global_type: &GlobalDescriptor) -> Result<GlobalRef, Error> {
|
||||||
&self,
|
Err(Error::Instantiation(format!("Export {} not found", field_name)))
|
||||||
field_name: &str,
|
|
||||||
_global_type: &GlobalDescriptor,
|
|
||||||
) -> Result<GlobalRef, Error> {
|
|
||||||
Err(Error::Instantiation(format!(
|
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a memory.
|
/// Resolve a memory.
|
||||||
|
@ -228,15 +204,8 @@ pub trait ModuleImportResolver {
|
||||||
/// See [`ImportResolver::resolve_memory`] for details.
|
/// See [`ImportResolver::resolve_memory`] for details.
|
||||||
///
|
///
|
||||||
/// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory
|
/// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory
|
||||||
fn resolve_memory(
|
fn resolve_memory(&self, field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
|
||||||
&self,
|
Err(Error::Instantiation(format!("Export {} not found", field_name)))
|
||||||
field_name: &str,
|
|
||||||
_memory_type: &MemoryDescriptor,
|
|
||||||
) -> Result<MemoryRef, Error> {
|
|
||||||
Err(Error::Instantiation(format!(
|
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a table.
|
/// Resolve a table.
|
||||||
|
@ -244,15 +213,8 @@ pub trait ModuleImportResolver {
|
||||||
/// See [`ImportResolver::resolve_table`] for details.
|
/// See [`ImportResolver::resolve_table`] for details.
|
||||||
///
|
///
|
||||||
/// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table
|
/// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table
|
||||||
fn resolve_table(
|
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> {
|
||||||
&self,
|
Err(Error::Instantiation(format!("Export {} not found", field_name)))
|
||||||
field_name: &str,
|
|
||||||
_table_type: &TableDescriptor,
|
|
||||||
) -> Result<TableRef, Error> {
|
|
||||||
Err(Error::Instantiation(format!(
|
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,46 +225,28 @@ impl ModuleImportResolver for ModuleRef {
|
||||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", 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(|| Error::Instantiation(format!("Export {} is not a function", field_name)))?)
|
||||||
Error::Instantiation(format!("Export {} is not a function", field_name))
|
|
||||||
})?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_global(
|
fn resolve_global(&self, field_name: &str, _global_type: &GlobalDescriptor) -> Result<GlobalRef, Error> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_global_type: &GlobalDescriptor,
|
|
||||||
) -> Result<GlobalRef, Error> {
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.export_by_name(field_name)
|
.export_by_name(field_name)
|
||||||
.ok_or_else(|| 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(|| Error::Instantiation(format!("Export {} is not a global", field_name)))?)
|
||||||
Error::Instantiation(format!("Export {} is not a global", field_name))
|
|
||||||
})?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_memory(
|
fn resolve_memory(&self, field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_memory_type: &MemoryDescriptor,
|
|
||||||
) -> Result<MemoryRef, Error> {
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.export_by_name(field_name)
|
.export_by_name(field_name)
|
||||||
.ok_or_else(|| 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(|| Error::Instantiation(format!("Export {} is not a memory", field_name)))?)
|
||||||
Error::Instantiation(format!("Export {} is not a memory", field_name))
|
|
||||||
})?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_table(
|
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_table_type: &TableDescriptor,
|
|
||||||
) -> Result<TableRef, Error> {
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.export_by_name(field_name)
|
.export_by_name(field_name)
|
||||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||||
|
|
10
src/isa.rs
10
src/isa.rs
|
@ -371,11 +371,9 @@ impl<'a> Iterator for InstructionIter<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
||||||
self.instructions
|
self.instructions.get(self.position as usize).map(|instruction| {
|
||||||
.get(self.position as usize)
|
self.position += 1;
|
||||||
.map(|instruction| {
|
instruction
|
||||||
self.position += 1;
|
})
|
||||||
instruction
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,8 +174,7 @@ 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 =
|
let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
|
||||||
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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,9 +208,7 @@ 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
|
let range = self.checked_region(&mut buffer, offset as usize, value.len())?.range();
|
||||||
.checked_region(&mut buffer, offset as usize, value.len())?
|
|
||||||
.range();
|
|
||||||
|
|
||||||
buffer[range].copy_from_slice(value);
|
buffer[range].copy_from_slice(value);
|
||||||
|
|
||||||
|
@ -241,9 +238,7 @@ impl MemoryInstance {
|
||||||
return Ok(size_before_grow);
|
return Ok(size_before_grow);
|
||||||
}
|
}
|
||||||
if additional > Pages(65536) {
|
if additional > Pages(65536) {
|
||||||
return Err(Error::Memory(format!(
|
return Err(Error::Memory(format!("Trying to grow memory by more than 65536 pages")));
|
||||||
"Trying to grow memory by more than 65536 pages"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_size: Pages = size_before_grow + additional;
|
let new_size: Pages = size_before_grow + additional;
|
||||||
|
@ -260,12 +255,7 @@ impl MemoryInstance {
|
||||||
Ok(size_before_grow)
|
Ok(size_before_grow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checked_region<B>(
|
fn checked_region<B>(&self, buffer: &mut B, offset: usize, size: usize) -> Result<CheckedRegion, Error>
|
||||||
&self,
|
|
||||||
buffer: &mut B,
|
|
||||||
offset: usize,
|
|
||||||
size: usize,
|
|
||||||
) -> Result<CheckedRegion, Error>
|
|
||||||
where
|
where
|
||||||
B: ::core::ops::DerefMut<Target = Vec<u8>>,
|
B: ::core::ops::DerefMut<Target = Vec<u8>>,
|
||||||
{
|
{
|
||||||
|
@ -365,8 +355,7 @@ 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) =
|
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||||
self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
::core::ptr::copy(
|
::core::ptr::copy(
|
||||||
|
@ -390,16 +379,10 @@ 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(
|
pub fn copy_nonoverlapping(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
|
||||||
&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) =
|
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||||
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!(
|
return Err(Error::Memory(format!(
|
||||||
|
@ -439,12 +422,8 @@ 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
|
let src_range = src.checked_region(&mut src_buffer, src_offset, len)?.range();
|
||||||
.checked_region(&mut src_buffer, src_offset, len)?
|
let dst_range = dst.checked_region(&mut dst_buffer, dst_offset, len)?.range();
|
||||||
.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]);
|
||||||
|
|
||||||
|
@ -588,8 +567,7 @@ 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)
|
mem.set(0, initial_content).expect("Successful initialize the memory");
|
||||||
.expect("Successful initialize the memory");
|
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,17 +619,12 @@ 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(&[
|
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
|
||||||
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!(
|
assert_eq!(dst.get(0, 10).unwrap(), &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]);
|
||||||
dst.get(0, 10).unwrap(),
|
|
||||||
&[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -675,25 +648,19 @@ 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(&[
|
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
|
||||||
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!(
|
assert_eq!(dst.get(0, 10).unwrap(), &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
|
||||||
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)
|
mem.clear(0, 0x4A, 10).expect("To successfully clear the memory");
|
||||||
.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]);
|
||||||
}
|
}
|
||||||
|
@ -701,12 +668,10 @@ 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])
|
mem.set(6, &[13, 17, 129]).expect("memory set should not fail");
|
||||||
.expect("memory set should not fail");
|
|
||||||
|
|
||||||
let mut data = [0u8; 2];
|
let mut data = [0u8; 2];
|
||||||
mem.get_into(7, &mut data[..])
|
mem.get_into(7, &mut data[..]).expect("get_into should not fail");
|
||||||
.expect("get_into should not fail");
|
|
||||||
|
|
||||||
assert_eq!(data, [17, 129]);
|
assert_eq!(data, [17, 129]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,46 +298,36 @@ impl ModuleInstance {
|
||||||
|
|
||||||
let code = loaded_module.code();
|
let code = loaded_module.code();
|
||||||
{
|
{
|
||||||
let funcs = module
|
let funcs = module.function_section().map(|fs| fs.entries()).unwrap_or(&[]);
|
||||||
.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(),
|
||||||
"Due to validation func and body counts must match"
|
"Due to validation func and body counts must match"
|
||||||
);
|
);
|
||||||
|
|
||||||
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
|
let signature = instance
|
||||||
.signature_by_index(ty.type_ref())
|
.signature_by_index(ty.type_ref())
|
||||||
.expect("Due to validation type should exists");
|
.expect("Due to validation type should exists");
|
||||||
let code = code.get(index).expect(
|
let code = code
|
||||||
"At func validation time labels are collected; Collected labels are added by index; qed",
|
.get(index)
|
||||||
).clone();
|
.expect("At func validation time labels are collected; Collected labels are added by index; qed")
|
||||||
|
.clone();
|
||||||
let func_body = FuncBody {
|
let func_body = FuncBody {
|
||||||
locals: body.locals().to_vec(),
|
locals: body.locals().to_vec(),
|
||||||
code: code,
|
code: code,
|
||||||
};
|
};
|
||||||
let func_instance =
|
let func_instance = FuncInstance::alloc_internal(Rc::downgrade(&instance.0), signature, func_body);
|
||||||
FuncInstance::alloc_internal(Rc::downgrade(&instance.0), signature, func_body);
|
|
||||||
instance.push_func(func_instance);
|
instance.push_func(func_instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 =
|
let table = TableInstance::alloc(table_type.limits().initial(), table_type.limits().maximum())?;
|
||||||
TableInstance::alloc(table_type.limits().initial(), table_type.limits().maximum())?;
|
|
||||||
instance.push_table(table);
|
instance.push_table(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
for memory_type in module
|
for memory_type in module.memory_section().map(|ms| ms.entries()).unwrap_or(&[]) {
|
||||||
.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));
|
||||||
|
|
||||||
|
@ -346,21 +336,13 @@ impl ModuleInstance {
|
||||||
instance.push_memory(memory);
|
instance.push_memory(memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
for global_entry in module
|
for global_entry in module.global_section().map(|gs| gs.entries()).unwrap_or(&[]) {
|
||||||
.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(init_val, global_entry.global_type().is_mutable());
|
let global = GlobalInstance::alloc(init_val, global_entry.global_type().is_mutable());
|
||||||
instance.push_global(global);
|
instance.push_global(global);
|
||||||
}
|
}
|
||||||
|
|
||||||
for export in module
|
for export in module.export_section().map(|es| es.entries()).unwrap_or(&[]) {
|
||||||
.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) => {
|
||||||
|
@ -408,11 +390,7 @@ 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
|
for element_segment in module.elements_section().map(|es| es.entries()).unwrap_or(&[]) {
|
||||||
.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"),
|
||||||
|
@ -424,12 +402,8 @@ impl ModuleInstance {
|
||||||
|
|
||||||
// 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
|
if offset_val as u64 + element_segment.members().len() as u64 > table_inst.current_size() as u64 {
|
||||||
> table_inst.current_size() as u64
|
return Err(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() {
|
||||||
|
@ -541,20 +515,17 @@ 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 =
|
let table = imports.resolve_table(module_name, field_name, &table_descriptor)?;
|
||||||
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 =
|
let memory = imports.resolve_memory(module_name, field_name, &memory_descriptor)?;
|
||||||
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 =
|
let global = imports.resolve_global(module_name, field_name, &global_descriptor)?;
|
||||||
imports.resolve_global(module_name, field_name, &global_descriptor)?;
|
|
||||||
ExternVal::Global(global)
|
ExternVal::Global(global)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -711,10 +682,7 @@ impl<'a> NotStartedModuleRef<'a> {
|
||||||
|
|
||||||
fn eval_init_expr(init_expr: &InitExpr, module: &ModuleInstance) -> RuntimeValue {
|
fn eval_init_expr(init_expr: &InitExpr, module: &ModuleInstance) -> RuntimeValue {
|
||||||
let code = init_expr.code();
|
let code = init_expr.code();
|
||||||
debug_assert!(
|
debug_assert!(code.len() == 2, "Due to validation `code`.len() should be 2");
|
||||||
code.len() == 2,
|
|
||||||
"Due to validation `code`.len() should be 2"
|
|
||||||
);
|
|
||||||
match code[0] {
|
match code[0] {
|
||||||
Instruction::I32Const(v) => v.into(),
|
Instruction::I32Const(v) => v.into(),
|
||||||
Instruction::I64Const(v) => v.into(),
|
Instruction::I64Const(v) => v.into(),
|
||||||
|
|
|
@ -12,10 +12,7 @@ macro_rules! impl_binop {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn $func_name(self, other: T) -> Self {
|
fn $func_name(self, other: T) -> Self {
|
||||||
$for(
|
$for($op::$func_name($is::from_bits(self.0), $is::from_bits(other.into().0)).to_bits())
|
||||||
$op::$func_name($is::from_bits(self.0), $is::from_bits(other.into().0))
|
|
||||||
.to_bits(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,12 +20,7 @@ macro_rules! impl_binop {
|
||||||
|
|
||||||
macro_rules! float {
|
macro_rules! float {
|
||||||
($for:ident, $rep:ident, $is:ident) => {
|
($for:ident, $rep:ident, $is:ident) => {
|
||||||
float!(
|
float!($for, $rep, $is, 1 << (::core::mem::size_of::<$is>() * 8 - 1));
|
||||||
$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)]
|
||||||
|
|
230
src/runner.rs
230
src/runner.rs
|
@ -13,8 +13,8 @@ use module::ModuleRef;
|
||||||
use nan_preserving_float::{F32, F64};
|
use nan_preserving_float::{F32, F64};
|
||||||
use parity_wasm::elements::Local;
|
use parity_wasm::elements::Local;
|
||||||
use value::{
|
use value::{
|
||||||
ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto,
|
ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto, TryTruncateInto,
|
||||||
TryTruncateInto, WrapInto,
|
WrapInto,
|
||||||
};
|
};
|
||||||
use {Signature, Trap, TrapKind, ValueType};
|
use {Signature, Trap, TrapKind, ValueType};
|
||||||
|
|
||||||
|
@ -211,9 +211,7 @@ impl Interpreter {
|
||||||
self.state = InterpreterState::Started;
|
self.state = InterpreterState::Started;
|
||||||
self.run_interpreter_loop(externals)?;
|
self.run_interpreter_loop(externals)?;
|
||||||
|
|
||||||
let opt_return_value = self
|
let opt_return_value = self.return_type.map(|vt| self.value_stack.pop().with_type(vt));
|
||||||
.return_type
|
|
||||||
.map(|vt| self.value_stack.pop().with_type(vt));
|
|
||||||
|
|
||||||
// Ensure that stack is empty after the execution. This is guaranteed by the validation properties.
|
// Ensure that stack is empty after the execution. This is guaranteed by the validation properties.
|
||||||
assert!(self.value_stack.len() == 0);
|
assert!(self.value_stack.len() == 0);
|
||||||
|
@ -235,16 +233,12 @@ impl Interpreter {
|
||||||
swap(&mut self.state, &mut resumable_state);
|
swap(&mut self.state, &mut resumable_state);
|
||||||
|
|
||||||
if let Some(return_val) = return_val {
|
if let Some(return_val) = return_val {
|
||||||
self.value_stack
|
self.value_stack.push(return_val.into()).map_err(Trap::new)?;
|
||||||
.push(return_val.into())
|
|
||||||
.map_err(Trap::new)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.run_interpreter_loop(externals)?;
|
self.run_interpreter_loop(externals)?;
|
||||||
|
|
||||||
let opt_return_value = self
|
let opt_return_value = self.return_type.map(|vt| self.value_stack.pop().with_type(vt));
|
||||||
.return_type
|
|
||||||
.map(|vt| self.value_stack.pop().with_type(vt));
|
|
||||||
|
|
||||||
// Ensure that stack is empty after the execution. This is guaranteed by the validation properties.
|
// Ensure that stack is empty after the execution. This is guaranteed by the validation properties.
|
||||||
assert!(self.value_stack.len() == 0);
|
assert!(self.value_stack.len() == 0);
|
||||||
|
@ -252,20 +246,16 @@ impl Interpreter {
|
||||||
Ok(opt_return_value)
|
Ok(opt_return_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_interpreter_loop<'a, E: Externals + 'a>(
|
fn run_interpreter_loop<'a, E: Externals + 'a>(&mut self, externals: &'a mut E) -> Result<(), Trap> {
|
||||||
&mut self,
|
|
||||||
externals: &'a mut E,
|
|
||||||
) -> Result<(), Trap> {
|
|
||||||
loop {
|
loop {
|
||||||
let mut function_context = self.call_stack.pop().expect(
|
let mut function_context = self
|
||||||
"on loop entry - not empty; on loop continue - checking for emptiness; qed",
|
.call_stack
|
||||||
);
|
.pop()
|
||||||
|
.expect("on loop entry - not empty; on loop continue - checking for emptiness; qed");
|
||||||
let function_ref = function_context.function.clone();
|
let function_ref = function_context.function.clone();
|
||||||
let function_body = function_ref
|
let function_body = function_ref
|
||||||
.body()
|
.body()
|
||||||
.expect(
|
.expect("Host functions checked in function_return below; Internal functions always have a body; qed");
|
||||||
"Host functions checked in function_return below; Internal functions always have a body; qed"
|
|
||||||
);
|
|
||||||
|
|
||||||
if !function_context.is_initialized() {
|
if !function_context.is_initialized() {
|
||||||
// Initialize stack frame for the function call.
|
// Initialize stack frame for the function call.
|
||||||
|
@ -300,18 +290,15 @@ impl Interpreter {
|
||||||
// We push the function context first. If the VM is not resumable, it does no harm. If it is, we then save the context here.
|
// We push the function context first. If the VM is not resumable, it does no harm. If it is, we then save the context here.
|
||||||
self.call_stack.push(function_context);
|
self.call_stack.push(function_context);
|
||||||
|
|
||||||
let return_val =
|
let return_val = match FuncInstance::invoke(&nested_func, &args, externals) {
|
||||||
match FuncInstance::invoke(&nested_func, &args, externals) {
|
Ok(val) => val,
|
||||||
Ok(val) => val,
|
Err(trap) => {
|
||||||
Err(trap) => {
|
if trap.kind().is_host() {
|
||||||
if trap.kind().is_host() {
|
self.state = InterpreterState::Resumable(nested_func.signature().return_type());
|
||||||
self.state = InterpreterState::Resumable(
|
|
||||||
nested_func.signature().return_type(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Err(trap);
|
|
||||||
}
|
}
|
||||||
};
|
return Err(trap);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Check if `return_val` matches the signature.
|
// Check if `return_val` matches the signature.
|
||||||
let value_ty = return_val.as_ref().map(|val| val.value_type());
|
let value_ty = return_val.as_ref().map(|val| val.value_type());
|
||||||
|
@ -321,9 +308,7 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(return_val) = return_val {
|
if let Some(return_val) = return_val {
|
||||||
self.value_stack
|
self.value_stack.push(return_val.into()).map_err(Trap::new)?;
|
||||||
.push(return_val.into())
|
|
||||||
.map_err(Trap::new)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,52 +377,26 @@ impl Interpreter {
|
||||||
&isa::Instruction::I64Load(offset) => self.run_load::<i64>(context, offset),
|
&isa::Instruction::I64Load(offset) => self.run_load::<i64>(context, offset),
|
||||||
&isa::Instruction::F32Load(offset) => self.run_load::<F32>(context, offset),
|
&isa::Instruction::F32Load(offset) => self.run_load::<F32>(context, offset),
|
||||||
&isa::Instruction::F64Load(offset) => self.run_load::<F64>(context, offset),
|
&isa::Instruction::F64Load(offset) => self.run_load::<F64>(context, offset),
|
||||||
&isa::Instruction::I32Load8S(offset) => {
|
&isa::Instruction::I32Load8S(offset) => self.run_load_extend::<i8, i32>(context, offset),
|
||||||
self.run_load_extend::<i8, i32>(context, offset)
|
&isa::Instruction::I32Load8U(offset) => self.run_load_extend::<u8, i32>(context, offset),
|
||||||
}
|
&isa::Instruction::I32Load16S(offset) => self.run_load_extend::<i16, i32>(context, offset),
|
||||||
&isa::Instruction::I32Load8U(offset) => {
|
&isa::Instruction::I32Load16U(offset) => self.run_load_extend::<u16, i32>(context, offset),
|
||||||
self.run_load_extend::<u8, i32>(context, offset)
|
&isa::Instruction::I64Load8S(offset) => self.run_load_extend::<i8, i64>(context, offset),
|
||||||
}
|
&isa::Instruction::I64Load8U(offset) => self.run_load_extend::<u8, i64>(context, offset),
|
||||||
&isa::Instruction::I32Load16S(offset) => {
|
&isa::Instruction::I64Load16S(offset) => self.run_load_extend::<i16, i64>(context, offset),
|
||||||
self.run_load_extend::<i16, i32>(context, offset)
|
&isa::Instruction::I64Load16U(offset) => self.run_load_extend::<u16, i64>(context, offset),
|
||||||
}
|
&isa::Instruction::I64Load32S(offset) => self.run_load_extend::<i32, i64>(context, offset),
|
||||||
&isa::Instruction::I32Load16U(offset) => {
|
&isa::Instruction::I64Load32U(offset) => self.run_load_extend::<u32, i64>(context, offset),
|
||||||
self.run_load_extend::<u16, i32>(context, offset)
|
|
||||||
}
|
|
||||||
&isa::Instruction::I64Load8S(offset) => {
|
|
||||||
self.run_load_extend::<i8, i64>(context, offset)
|
|
||||||
}
|
|
||||||
&isa::Instruction::I64Load8U(offset) => {
|
|
||||||
self.run_load_extend::<u8, i64>(context, offset)
|
|
||||||
}
|
|
||||||
&isa::Instruction::I64Load16S(offset) => {
|
|
||||||
self.run_load_extend::<i16, i64>(context, offset)
|
|
||||||
}
|
|
||||||
&isa::Instruction::I64Load16U(offset) => {
|
|
||||||
self.run_load_extend::<u16, i64>(context, offset)
|
|
||||||
}
|
|
||||||
&isa::Instruction::I64Load32S(offset) => {
|
|
||||||
self.run_load_extend::<i32, i64>(context, offset)
|
|
||||||
}
|
|
||||||
&isa::Instruction::I64Load32U(offset) => {
|
|
||||||
self.run_load_extend::<u32, i64>(context, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
&isa::Instruction::I32Store(offset) => self.run_store::<i32>(context, offset),
|
&isa::Instruction::I32Store(offset) => self.run_store::<i32>(context, offset),
|
||||||
&isa::Instruction::I64Store(offset) => self.run_store::<i64>(context, offset),
|
&isa::Instruction::I64Store(offset) => self.run_store::<i64>(context, offset),
|
||||||
&isa::Instruction::F32Store(offset) => self.run_store::<F32>(context, offset),
|
&isa::Instruction::F32Store(offset) => self.run_store::<F32>(context, offset),
|
||||||
&isa::Instruction::F64Store(offset) => self.run_store::<F64>(context, offset),
|
&isa::Instruction::F64Store(offset) => self.run_store::<F64>(context, offset),
|
||||||
&isa::Instruction::I32Store8(offset) => self.run_store_wrap::<i32, i8>(context, offset),
|
&isa::Instruction::I32Store8(offset) => self.run_store_wrap::<i32, i8>(context, offset),
|
||||||
&isa::Instruction::I32Store16(offset) => {
|
&isa::Instruction::I32Store16(offset) => self.run_store_wrap::<i32, i16>(context, offset),
|
||||||
self.run_store_wrap::<i32, i16>(context, offset)
|
|
||||||
}
|
|
||||||
&isa::Instruction::I64Store8(offset) => self.run_store_wrap::<i64, i8>(context, offset),
|
&isa::Instruction::I64Store8(offset) => self.run_store_wrap::<i64, i8>(context, offset),
|
||||||
&isa::Instruction::I64Store16(offset) => {
|
&isa::Instruction::I64Store16(offset) => self.run_store_wrap::<i64, i16>(context, offset),
|
||||||
self.run_store_wrap::<i64, i16>(context, offset)
|
&isa::Instruction::I64Store32(offset) => self.run_store_wrap::<i64, i32>(context, offset),
|
||||||
}
|
|
||||||
&isa::Instruction::I64Store32(offset) => {
|
|
||||||
self.run_store_wrap::<i64, i32>(context, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
&isa::Instruction::CurrentMemory => self.run_current_memory(context),
|
&isa::Instruction::CurrentMemory => self.run_current_memory(context),
|
||||||
&isa::Instruction::GrowMemory => self.run_grow_memory(context),
|
&isa::Instruction::GrowMemory => self.run_grow_memory(context),
|
||||||
|
@ -582,18 +541,11 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_unreachable(
|
fn run_unreachable(&mut self, _context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
|
||||||
&mut self,
|
|
||||||
_context: &mut FunctionContext,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind> {
|
|
||||||
Err(TrapKind::Unreachable)
|
Err(TrapKind::Unreachable)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_br(
|
fn run_br(&mut self, _context: &mut FunctionContext, target: isa::Target) -> Result<InstructionOutcome, TrapKind> {
|
||||||
&mut self,
|
|
||||||
_context: &mut FunctionContext,
|
|
||||||
target: isa::Target,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind> {
|
|
||||||
Ok(InstructionOutcome::Branch(target))
|
Ok(InstructionOutcome::Branch(target))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,11 +585,7 @@ impl Interpreter {
|
||||||
Ok(InstructionOutcome::Return(drop_keep))
|
Ok(InstructionOutcome::Return(drop_keep))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_call(
|
fn run_call(&mut self, context: &mut FunctionContext, func_idx: u32) -> Result<InstructionOutcome, TrapKind> {
|
||||||
&mut self,
|
|
||||||
context: &mut FunctionContext,
|
|
||||||
func_idx: u32,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind> {
|
|
||||||
let func = context
|
let func = context
|
||||||
.module()
|
.module()
|
||||||
.func_by_index(func_idx)
|
.func_by_index(func_idx)
|
||||||
|
@ -707,11 +655,7 @@ impl Interpreter {
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_get_global(
|
fn run_get_global(&mut self, context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, TrapKind> {
|
||||||
&mut self,
|
|
||||||
context: &mut FunctionContext,
|
|
||||||
index: u32,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind> {
|
|
||||||
let global = context
|
let global = context
|
||||||
.module()
|
.module()
|
||||||
.global_by_index(index)
|
.global_by_index(index)
|
||||||
|
@ -721,11 +665,7 @@ impl Interpreter {
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_set_global(
|
fn run_set_global(&mut self, context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, TrapKind> {
|
||||||
&mut self,
|
|
||||||
context: &mut FunctionContext,
|
|
||||||
index: u32,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind> {
|
|
||||||
let val = self.value_stack.pop();
|
let val = self.value_stack.pop();
|
||||||
let global = context
|
let global = context
|
||||||
.module()
|
.module()
|
||||||
|
@ -737,23 +677,15 @@ impl Interpreter {
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_load<T>(
|
fn run_load<T>(&mut self, context: &mut FunctionContext, offset: u32) -> Result<InstructionOutcome, TrapKind>
|
||||||
&mut self,
|
|
||||||
context: &mut FunctionContext,
|
|
||||||
offset: u32,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind>
|
|
||||||
where
|
where
|
||||||
RuntimeValueInternal: From<T>,
|
RuntimeValueInternal: From<T>,
|
||||||
T: LittleEndianConvert,
|
T: LittleEndianConvert,
|
||||||
{
|
{
|
||||||
let raw_address = self.value_stack.pop_as();
|
let raw_address = self.value_stack.pop_as();
|
||||||
let address = effective_address(offset, raw_address)?;
|
let address = effective_address(offset, raw_address)?;
|
||||||
let m = context
|
let m = context.memory().expect("Due to validation memory should exists");
|
||||||
.memory()
|
let n: T = m.get_value(address).map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
|
||||||
.expect("Due to validation memory should exists");
|
|
||||||
let n: T = m
|
|
||||||
.get_value(address)
|
|
||||||
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
|
|
||||||
self.value_stack.push(n.into())?;
|
self.value_stack.push(n.into())?;
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
@ -770,12 +702,8 @@ impl Interpreter {
|
||||||
{
|
{
|
||||||
let raw_address = self.value_stack.pop_as();
|
let raw_address = self.value_stack.pop_as();
|
||||||
let address = effective_address(offset, raw_address)?;
|
let address = effective_address(offset, raw_address)?;
|
||||||
let m = context
|
let m = context.memory().expect("Due to validation memory should exists");
|
||||||
.memory()
|
let v: T = m.get_value(address).map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
|
||||||
.expect("Due to validation memory should exists");
|
|
||||||
let v: T = m
|
|
||||||
.get_value(address)
|
|
||||||
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
|
|
||||||
let stack_value: U = v.extend_into();
|
let stack_value: U = v.extend_into();
|
||||||
self.value_stack
|
self.value_stack
|
||||||
.push(stack_value.into())
|
.push(stack_value.into())
|
||||||
|
@ -783,11 +711,7 @@ impl Interpreter {
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_store<T>(
|
fn run_store<T>(&mut self, context: &mut FunctionContext, offset: u32) -> Result<InstructionOutcome, TrapKind>
|
||||||
&mut self,
|
|
||||||
context: &mut FunctionContext,
|
|
||||||
offset: u32,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind>
|
|
||||||
where
|
where
|
||||||
T: FromRuntimeValueInternal,
|
T: FromRuntimeValueInternal,
|
||||||
T: LittleEndianConvert,
|
T: LittleEndianConvert,
|
||||||
|
@ -796,9 +720,7 @@ impl Interpreter {
|
||||||
let raw_address = self.value_stack.pop_as::<u32>();
|
let raw_address = self.value_stack.pop_as::<u32>();
|
||||||
let address = effective_address(offset, raw_address)?;
|
let address = effective_address(offset, raw_address)?;
|
||||||
|
|
||||||
let m = context
|
let m = context.memory().expect("Due to validation memory should exists");
|
||||||
.memory()
|
|
||||||
.expect("Due to validation memory should exists");
|
|
||||||
m.set_value(address, stack_value)
|
m.set_value(address, stack_value)
|
||||||
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
|
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
|
@ -818,34 +740,22 @@ impl Interpreter {
|
||||||
let stack_value = stack_value.wrap_into();
|
let stack_value = stack_value.wrap_into();
|
||||||
let raw_address = self.value_stack.pop_as::<u32>();
|
let raw_address = self.value_stack.pop_as::<u32>();
|
||||||
let address = effective_address(offset, raw_address)?;
|
let address = effective_address(offset, raw_address)?;
|
||||||
let m = context
|
let m = context.memory().expect("Due to validation memory should exists");
|
||||||
.memory()
|
|
||||||
.expect("Due to validation memory should exists");
|
|
||||||
m.set_value(address, stack_value)
|
m.set_value(address, stack_value)
|
||||||
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
|
.map_err(|_| TrapKind::MemoryAccessOutOfBounds)?;
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_current_memory(
|
fn run_current_memory(&mut self, context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
|
||||||
&mut self,
|
let m = context.memory().expect("Due to validation memory should exists");
|
||||||
context: &mut FunctionContext,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind> {
|
|
||||||
let m = context
|
|
||||||
.memory()
|
|
||||||
.expect("Due to validation memory should exists");
|
|
||||||
let s = m.current_size().0;
|
let s = m.current_size().0;
|
||||||
self.value_stack.push(RuntimeValueInternal(s as _))?;
|
self.value_stack.push(RuntimeValueInternal(s as _))?;
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_grow_memory(
|
fn run_grow_memory(&mut self, context: &mut FunctionContext) -> Result<InstructionOutcome, TrapKind> {
|
||||||
&mut self,
|
|
||||||
context: &mut FunctionContext,
|
|
||||||
) -> Result<InstructionOutcome, TrapKind> {
|
|
||||||
let pages: u32 = self.value_stack.pop_as();
|
let pages: u32 = self.value_stack.pop_as();
|
||||||
let m = context
|
let m = context.memory().expect("Due to validation memory should exists");
|
||||||
.memory()
|
|
||||||
.expect("Due to validation memory should exists");
|
|
||||||
let m = match m.grow(Pages(pages as usize)) {
|
let m = match m.grow(Pages(pages as usize)) {
|
||||||
Ok(Pages(new_size)) => new_size as u32,
|
Ok(Pages(new_size)) => new_size as u32,
|
||||||
Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error.
|
Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error.
|
||||||
|
@ -1279,11 +1189,7 @@ impl FunctionContext {
|
||||||
self.is_initialized
|
self.is_initialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize(
|
pub fn initialize(&mut self, locals: &[Local], value_stack: &mut ValueStack) -> Result<(), TrapKind> {
|
||||||
&mut self,
|
|
||||||
locals: &[Local],
|
|
||||||
value_stack: &mut ValueStack,
|
|
||||||
) -> Result<(), TrapKind> {
|
|
||||||
debug_assert!(!self.is_initialized);
|
debug_assert!(!self.is_initialized);
|
||||||
|
|
||||||
let num_locals = locals.iter().map(|l| l.count() as usize).sum();
|
let num_locals = locals.iter().map(|l| l.count() as usize).sum();
|
||||||
|
@ -1291,9 +1197,7 @@ impl FunctionContext {
|
||||||
|
|
||||||
// TODO: Replace with extend.
|
// TODO: Replace with extend.
|
||||||
for local in locals {
|
for local in locals {
|
||||||
value_stack
|
value_stack.push(local).map_err(|_| TrapKind::StackOverflow)?;
|
||||||
.push(local)
|
|
||||||
.map_err(|_| TrapKind::StackOverflow)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.is_initialized = true;
|
self.is_initialized = true;
|
||||||
|
@ -1322,10 +1226,7 @@ fn effective_address(address: u32, offset: u32) -> Result<u32, TrapKind> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_function_args(
|
fn prepare_function_args(signature: &Signature, caller_stack: &mut ValueStack) -> Vec<RuntimeValue> {
|
||||||
signature: &Signature,
|
|
||||||
caller_stack: &mut ValueStack,
|
|
||||||
) -> Vec<RuntimeValue> {
|
|
||||||
let mut out = signature
|
let mut out = signature
|
||||||
.params()
|
.params()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1341,14 +1242,10 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu
|
||||||
return Err(TrapKind::UnexpectedSignature.into());
|
return Err(TrapKind::UnexpectedSignature.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if signature
|
if signature.params().iter().zip(args).any(|(expected_type, param_value)| {
|
||||||
.params()
|
let actual_type = param_value.value_type();
|
||||||
.iter()
|
&actual_type != expected_type
|
||||||
.zip(args)
|
}) {
|
||||||
.any(|(expected_type, param_value)| {
|
|
||||||
let actual_type = param_value.value_type();
|
|
||||||
&actual_type != expected_type
|
|
||||||
}) {
|
|
||||||
return Err(TrapKind::UnexpectedSignature.into());
|
return Err(TrapKind::UnexpectedSignature.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1405,13 +1302,7 @@ impl ValueStack {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pop_triple(
|
fn pop_triple(&mut self) -> (RuntimeValueInternal, RuntimeValueInternal, RuntimeValueInternal) {
|
||||||
&mut self,
|
|
||||||
) -> (
|
|
||||||
RuntimeValueInternal,
|
|
||||||
RuntimeValueInternal,
|
|
||||||
RuntimeValueInternal,
|
|
||||||
) {
|
|
||||||
let right = self.pop();
|
let right = self.pop();
|
||||||
let mid = self.pop();
|
let mid = self.pop();
|
||||||
let left = self.pop();
|
let left = self.pop();
|
||||||
|
@ -1440,10 +1331,7 @@ impl ValueStack {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push(&mut self, value: RuntimeValueInternal) -> Result<(), TrapKind> {
|
fn push(&mut self, value: RuntimeValueInternal) -> Result<(), TrapKind> {
|
||||||
let cell = self
|
let cell = self.buf.get_mut(self.sp).ok_or_else(|| TrapKind::StackOverflow)?;
|
||||||
.buf
|
|
||||||
.get_mut(self.sp)
|
|
||||||
.ok_or_else(|| TrapKind::StackOverflow)?;
|
|
||||||
*cell = value;
|
*cell = value;
|
||||||
self.sp += 1;
|
self.sp += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -109,13 +109,8 @@ impl TableInstance {
|
||||||
let new_size = self
|
let new_size = self
|
||||||
.current_size()
|
.current_size()
|
||||||
.checked_add(by)
|
.checked_add(by)
|
||||||
.and_then(|new_size| {
|
.and_then(|new_size| if maximum_size < new_size { None } else { Some(new_size) })
|
||||||
if maximum_size < new_size {
|
.ok_or_else(|| {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(new_size)
|
|
||||||
}
|
|
||||||
}).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,
|
||||||
|
|
|
@ -2,9 +2,9 @@ use super::parse_wat;
|
||||||
use memory_units::Pages;
|
use memory_units::Pages;
|
||||||
use types::ValueType;
|
use types::ValueType;
|
||||||
use {
|
use {
|
||||||
Error, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, MemoryDescriptor,
|
Error, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef,
|
||||||
MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, ResumableError,
|
ModuleImportResolver, ModuleInstance, ModuleRef, ResumableError, RuntimeArgs, RuntimeValue, Signature,
|
||||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap, TrapKind,
|
TableDescriptor, TableInstance, TableRef, Trap, TrapKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -85,11 +85,7 @@ const RECURSE_FUNC_INDEX: usize = 4;
|
||||||
const TRAP_SUB_FUNC_INDEX: usize = 5;
|
const TRAP_SUB_FUNC_INDEX: usize = 5;
|
||||||
|
|
||||||
impl Externals for TestHost {
|
impl Externals for TestHost {
|
||||||
fn invoke_index(
|
fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
&mut self,
|
|
||||||
index: usize,
|
|
||||||
args: RuntimeArgs,
|
|
||||||
) -> Result<Option<RuntimeValue>, Trap> {
|
|
||||||
match index {
|
match index {
|
||||||
SUB_FUNC_INDEX => {
|
SUB_FUNC_INDEX => {
|
||||||
let a: i32 = args.nth(0);
|
let a: i32 = args.nth(0);
|
||||||
|
@ -131,9 +127,7 @@ impl Externals for TestHost {
|
||||||
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
|
let val = args.nth_value_checked(0).expect("Exactly one argument expected");
|
||||||
.nth_value_checked(0)
|
|
||||||
.expect("Exactly one argument expected");
|
|
||||||
|
|
||||||
let instance = self
|
let instance = self
|
||||||
.instance
|
.instance
|
||||||
|
@ -146,9 +140,7 @@ 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(
|
return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into());
|
||||||
TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(Some(result))
|
Ok(Some(result))
|
||||||
}
|
}
|
||||||
|
@ -198,12 +190,7 @@ impl ModuleImportResolver for TestHost {
|
||||||
"get_mem" => GET_MEM_FUNC_INDEX,
|
"get_mem" => GET_MEM_FUNC_INDEX,
|
||||||
"recurse" => RECURSE_FUNC_INDEX,
|
"recurse" => RECURSE_FUNC_INDEX,
|
||||||
"trap_sub" => TRAP_SUB_FUNC_INDEX,
|
"trap_sub" => TRAP_SUB_FUNC_INDEX,
|
||||||
_ => {
|
_ => return Err(Error::Instantiation(format!("Export {} not found", field_name))),
|
||||||
return Err(Error::Instantiation(format!(
|
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !self.check_signature(index, signature) {
|
if !self.check_signature(index, signature) {
|
||||||
|
@ -216,15 +203,8 @@ impl ModuleImportResolver for TestHost {
|
||||||
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_memory(
|
fn resolve_memory(&self, field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
|
||||||
&self,
|
Err(Error::Instantiation(format!("Export {} not found", field_name)))
|
||||||
field_name: &str,
|
|
||||||
_memory_type: &MemoryDescriptor,
|
|
||||||
) -> Result<MemoryRef, Error> {
|
|
||||||
Err(Error::Instantiation(format!(
|
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,10 +464,7 @@ fn defer_providing_externals() {
|
||||||
impl ModuleImportResolver for HostImportResolver {
|
impl ModuleImportResolver for HostImportResolver {
|
||||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||||
if field_name != "inc" {
|
if field_name != "inc" {
|
||||||
return Err(Error::Instantiation(format!(
|
return Err(Error::Instantiation(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!(
|
||||||
|
@ -499,18 +476,11 @@ fn defer_providing_externals() {
|
||||||
Ok(FuncInstance::alloc_host(signature.clone(), INC_FUNC_INDEX))
|
Ok(FuncInstance::alloc_host(signature.clone(), INC_FUNC_INDEX))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_memory(
|
fn resolve_memory(&self, field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_memory_type: &MemoryDescriptor,
|
|
||||||
) -> Result<MemoryRef, Error> {
|
|
||||||
if field_name == "mem" {
|
if field_name == "mem" {
|
||||||
Ok(self.mem.clone())
|
Ok(self.mem.clone())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Instantiation(format!(
|
Err(Error::Instantiation(format!("Export {} not found", field_name)))
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,11 +492,7 @@ fn defer_providing_externals() {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Externals for HostExternals<'a> {
|
impl<'a> Externals for HostExternals<'a> {
|
||||||
fn invoke_index(
|
fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
&mut self,
|
|
||||||
index: usize,
|
|
||||||
args: RuntimeArgs,
|
|
||||||
) -> Result<Option<RuntimeValue>, Trap> {
|
|
||||||
match index {
|
match index {
|
||||||
INC_FUNC_INDEX => {
|
INC_FUNC_INDEX => {
|
||||||
let a = args.nth::<u32>(0);
|
let a = args.nth::<u32>(0);
|
||||||
|
@ -569,12 +535,8 @@ fn defer_providing_externals() {
|
||||||
{
|
{
|
||||||
let mut host_externals = HostExternals { acc: &mut acc };
|
let mut host_externals = HostExternals { acc: &mut acc };
|
||||||
|
|
||||||
instance
|
instance.invoke_export("test", &[], &mut host_externals).unwrap(); // acc += 1;
|
||||||
.invoke_export("test", &[], &mut host_externals)
|
instance.invoke_export("test", &[], &mut host_externals).unwrap(); // acc += 1;
|
||||||
.unwrap(); // acc += 1;
|
|
||||||
instance
|
|
||||||
.invoke_export("test", &[], &mut host_externals)
|
|
||||||
.unwrap(); // acc += 1;
|
|
||||||
}
|
}
|
||||||
assert_eq!(acc, 91);
|
assert_eq!(acc, 91);
|
||||||
}
|
}
|
||||||
|
@ -587,11 +549,7 @@ fn two_envs_one_externals() {
|
||||||
struct HostExternals;
|
struct HostExternals;
|
||||||
|
|
||||||
impl Externals for HostExternals {
|
impl Externals for HostExternals {
|
||||||
fn invoke_index(
|
fn invoke_index(&mut self, index: usize, _args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
&mut self,
|
|
||||||
index: usize,
|
|
||||||
_args: RuntimeArgs,
|
|
||||||
) -> Result<Option<RuntimeValue>, Trap> {
|
|
||||||
match index {
|
match index {
|
||||||
PRIVILEGED_FUNC_INDEX => {
|
PRIVILEGED_FUNC_INDEX => {
|
||||||
println!("privileged!");
|
println!("privileged!");
|
||||||
|
@ -611,12 +569,7 @@ fn two_envs_one_externals() {
|
||||||
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(format!("Export {} not found", field_name))),
|
||||||
return Err(Error::Instantiation(format!(
|
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
||||||
|
@ -632,12 +585,7 @@ fn two_envs_one_externals() {
|
||||||
"'priveleged' can be imported only in privileged context".into(),
|
"'priveleged' can be imported only in privileged context".into(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => return Err(Error::Instantiation(format!("Export {} not found", field_name))),
|
||||||
return Err(Error::Instantiation(format!(
|
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
||||||
|
@ -701,11 +649,7 @@ fn dynamically_add_host_func() {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Externals for HostExternals {
|
impl Externals for HostExternals {
|
||||||
fn invoke_index(
|
fn invoke_index(&mut self, index: usize, _args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
&mut self,
|
|
||||||
index: usize,
|
|
||||||
_args: RuntimeArgs,
|
|
||||||
) -> Result<Option<RuntimeValue>, Trap> {
|
|
||||||
match index {
|
match index {
|
||||||
ADD_FUNC_FUNC_INDEX => {
|
ADD_FUNC_FUNC_INDEX => {
|
||||||
// Allocate indicies for the new function.
|
// Allocate indicies for the new function.
|
||||||
|
@ -724,9 +668,7 @@ fn dynamically_add_host_func() {
|
||||||
|
|
||||||
Ok(Some(RuntimeValue::I32(table_index as i32)))
|
Ok(Some(RuntimeValue::I32(table_index as i32)))
|
||||||
}
|
}
|
||||||
index if index as u32 <= self.added_funcs => {
|
index if index as u32 <= self.added_funcs => Ok(Some(RuntimeValue::I32(index as i32))),
|
||||||
Ok(Some(RuntimeValue::I32(index as i32)))
|
|
||||||
}
|
|
||||||
_ => panic!("'env' module doesn't provide function at index {}", index),
|
_ => panic!("'env' module doesn't provide function at index {}", index),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -736,28 +678,16 @@ fn dynamically_add_host_func() {
|
||||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||||
let index = match field_name {
|
let index = match field_name {
|
||||||
"add_func" => ADD_FUNC_FUNC_INDEX,
|
"add_func" => ADD_FUNC_FUNC_INDEX,
|
||||||
_ => {
|
_ => return Err(Error::Instantiation(format!("Export {} not found", field_name))),
|
||||||
return Err(Error::Instantiation(format!(
|
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_table(
|
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_table_type: &TableDescriptor,
|
|
||||||
) -> Result<TableRef, Error> {
|
|
||||||
if field_name == "table" {
|
if field_name == "table" {
|
||||||
Ok(self.table.clone())
|
Ok(self.table.clone())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Instantiation(format!(
|
Err(Error::Instantiation(format!("Export {} not found", field_name)))
|
||||||
"Export {} not found",
|
|
||||||
field_name
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,11 +716,9 @@ fn dynamically_add_host_func() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
let instance = ModuleInstance::new(
|
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &host_externals))
|
||||||
&module,
|
.expect("Failed to instantiate module")
|
||||||
&ImportsBuilder::new().with_resolver("env", &host_externals),
|
.assert_no_start();
|
||||||
).expect("Failed to instantiate module")
|
|
||||||
.assert_no_start();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
instance
|
instance
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use memory_units::Pages;
|
use memory_units::Pages;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use {
|
use {
|
||||||
Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor,
|
Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor, MemoryInstance,
|
||||||
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals,
|
MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, Signature, TableDescriptor,
|
||||||
RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
|
TableInstance, TableRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Env {
|
struct Env {
|
||||||
|
@ -26,16 +26,10 @@ impl Env {
|
||||||
|
|
||||||
impl ModuleImportResolver for Env {
|
impl ModuleImportResolver for Env {
|
||||||
fn resolve_func(&self, _field_name: &str, _func_type: &Signature) -> Result<FuncRef, Error> {
|
fn resolve_func(&self, _field_name: &str, _func_type: &Signature) -> Result<FuncRef, Error> {
|
||||||
Err(Error::Instantiation(
|
Err(Error::Instantiation("env module doesn't provide any functions".into()))
|
||||||
"env module doesn't provide any functions".into(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_global(
|
fn resolve_global(&self, field_name: &str, _global_type: &GlobalDescriptor) -> Result<GlobalRef, Error> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_global_type: &GlobalDescriptor,
|
|
||||||
) -> Result<GlobalRef, Error> {
|
|
||||||
match field_name {
|
match field_name {
|
||||||
"tableBase" => Ok(self.table_base.clone()),
|
"tableBase" => Ok(self.table_base.clone()),
|
||||||
"memoryBase" => Ok(self.memory_base.clone()),
|
"memoryBase" => Ok(self.memory_base.clone()),
|
||||||
|
@ -46,11 +40,7 @@ impl ModuleImportResolver for Env {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_memory(
|
fn resolve_memory(&self, field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_memory_type: &MemoryDescriptor,
|
|
||||||
) -> Result<MemoryRef, Error> {
|
|
||||||
match field_name {
|
match field_name {
|
||||||
"memory" => Ok(self.memory.clone()),
|
"memory" => Ok(self.memory.clone()),
|
||||||
_ => Err(Error::Instantiation(format!(
|
_ => Err(Error::Instantiation(format!(
|
||||||
|
@ -60,11 +50,7 @@ impl ModuleImportResolver for Env {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_table(
|
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> {
|
||||||
&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(format!(
|
_ => Err(Error::Instantiation(format!(
|
||||||
|
@ -134,10 +120,7 @@ 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 = &[
|
let args = &[RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)];
|
||||||
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");
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use alloc::borrow::Cow;
|
use alloc::borrow::Cow;
|
||||||
|
|
||||||
use parity_wasm::elements::{
|
use parity_wasm::elements::{FunctionType, GlobalType, MemoryType, TableType, ValueType as EValueType};
|
||||||
FunctionType, GlobalType, MemoryType, TableType, ValueType as EValueType,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Signature of a [function].
|
/// Signature of a [function].
|
||||||
///
|
///
|
||||||
|
@ -37,10 +35,7 @@ impl Signature {
|
||||||
/// let dynamic_params = vec![ValueType::I64];
|
/// let dynamic_params = vec![ValueType::I64];
|
||||||
/// let s3 = Signature::new(dynamic_params, None);
|
/// let s3 = Signature::new(dynamic_params, None);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new<C: Into<Cow<'static, [ValueType]>>>(
|
pub fn new<C: Into<Cow<'static, [ValueType]>>>(params: C, return_type: Option<ValueType>) -> Signature {
|
||||||
params: C,
|
|
||||||
return_type: Option<ValueType>,
|
|
||||||
) -> Signature {
|
|
||||||
Signature {
|
Signature {
|
||||||
params: params.into(),
|
params: params.into(),
|
||||||
return_type: return_type,
|
return_type: return_type,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use alloc::prelude::*;
|
use alloc::prelude::*;
|
||||||
use parity_wasm::elements::{
|
use parity_wasm::elements::{BlockType, FunctionType, GlobalType, MemoryType, TableType, ValueType};
|
||||||
BlockType, FunctionType, GlobalType, MemoryType, TableType, ValueType,
|
|
||||||
};
|
|
||||||
use validation::Error;
|
use validation::Error;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -63,10 +61,7 @@ impl ModuleContext {
|
||||||
.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
|
let return_ty = ty.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult);
|
||||||
.return_type()
|
|
||||||
.map(BlockType::Value)
|
|
||||||
.unwrap_or(BlockType::NoResult);
|
|
||||||
Ok((params, return_ty))
|
Ok((params, return_ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,11 +158,7 @@ enum Outcome {
|
||||||
pub struct FunctionReader;
|
pub struct FunctionReader;
|
||||||
|
|
||||||
impl FunctionReader {
|
impl FunctionReader {
|
||||||
pub fn read_function(
|
pub fn read_function(module: &ModuleContext, func: &Func, body: &FuncBody) -> Result<isa::Instructions, Error> {
|
||||||
module: &ModuleContext,
|
|
||||||
func: &Func,
|
|
||||||
body: &FuncBody,
|
|
||||||
) -> Result<isa::Instructions, Error> {
|
|
||||||
let (params, result_ty) = module.require_function_type(func.type_ref())?;
|
let (params, result_ty) = module.require_function_type(func.type_ref())?;
|
||||||
|
|
||||||
let ins_size_estimate = body.code().elements().len();
|
let ins_size_estimate = body.code().elements().len();
|
||||||
|
@ -190,10 +186,7 @@ impl FunctionReader {
|
||||||
Ok(context.into_code())
|
Ok(context.into_code())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_function_body(
|
fn read_function_body(context: &mut FunctionValidationContext, body: &[Instruction]) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
body: &[Instruction],
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let body_len = body.len();
|
let body_len = body.len();
|
||||||
if body_len == 0 {
|
if body_len == 0 {
|
||||||
return Err(Error("Non-empty function body expected".into()));
|
return Err(Error("Non-empty function body expected".into()));
|
||||||
|
@ -202,19 +195,16 @@ impl FunctionReader {
|
||||||
loop {
|
loop {
|
||||||
let instruction = &body[context.position];
|
let instruction = &body[context.position];
|
||||||
|
|
||||||
let outcome =
|
let outcome = FunctionReader::read_instruction(context, instruction).map_err(|err| {
|
||||||
FunctionReader::read_instruction(context, instruction).map_err(|err| {
|
Error(format!(
|
||||||
Error(format!(
|
"At instruction {:?}(@{}): {}",
|
||||||
"At instruction {:?}(@{}): {}",
|
instruction, context.position, err
|
||||||
instruction, context.position, err
|
))
|
||||||
))
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
match outcome {
|
match outcome {
|
||||||
Outcome::NextInstruction => (),
|
Outcome::NextInstruction => (),
|
||||||
Outcome::Unreachable => {
|
Outcome::Unreachable => make_top_frame_polymorphic(&mut context.value_stack, &mut context.frame_stack),
|
||||||
make_top_frame_polymorphic(&mut context.value_stack, &mut context.frame_stack)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.position += 1;
|
context.position += 1;
|
||||||
|
@ -224,10 +214,7 @@ impl FunctionReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_instruction(
|
fn read_instruction(context: &mut FunctionValidationContext, instruction: &Instruction) -> Result<Outcome, Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
instruction: &Instruction,
|
|
||||||
) -> Result<Outcome, Error> {
|
|
||||||
use self::Instruction::*;
|
use self::Instruction::*;
|
||||||
match *instruction {
|
match *instruction {
|
||||||
// Nop instruction doesn't do anything. It is safe to just skip it.
|
// Nop instruction doesn't do anything. It is safe to just skip it.
|
||||||
|
@ -267,11 +254,7 @@ impl FunctionReader {
|
||||||
let if_not = context.sink.new_label();
|
let if_not = context.sink.new_label();
|
||||||
let end_label = context.sink.new_label();
|
let end_label = context.sink.new_label();
|
||||||
|
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
ValueType::I32.into(),
|
|
||||||
)?;
|
|
||||||
push_label(
|
push_label(
|
||||||
BlockFrameType::IfTrue { if_not, end_label },
|
BlockFrameType::IfTrue { if_not, end_label },
|
||||||
block_type,
|
block_type,
|
||||||
|
@ -334,9 +317,9 @@ impl FunctionReader {
|
||||||
// A `if` without an `else` can't return a result.
|
// A `if` without an `else` can't return a result.
|
||||||
if block_type != BlockType::NoResult {
|
if block_type != BlockType::NoResult {
|
||||||
return Err(Error(format!(
|
return Err(Error(format!(
|
||||||
"If block without else required to have NoResult block type. But it has {:?} type",
|
"If block without else required to have NoResult block type. But it has {:?} type",
|
||||||
block_type
|
block_type
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump
|
// Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump
|
||||||
|
@ -356,19 +339,11 @@ impl FunctionReader {
|
||||||
|
|
||||||
// Check the return type.
|
// Check the return type.
|
||||||
if let BlockType::Value(value_type) = context.return_type()? {
|
if let BlockType::Value(value_type) = context.return_type()? {
|
||||||
tee_value(
|
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit the return instruction.
|
// Emit the return instruction.
|
||||||
let drop_keep = drop_keep_return(
|
let drop_keep = drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack);
|
||||||
&context.locals,
|
|
||||||
&context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
);
|
|
||||||
context.sink.emit(isa::Instruction::Return(drop_keep));
|
context.sink.emit(isa::Instruction::Return(drop_keep));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,23 +376,17 @@ impl FunctionReader {
|
||||||
let target = require_target(*depth, &context.value_stack, &context.frame_stack);
|
let target = require_target(*depth, &context.value_stack, &context.frame_stack);
|
||||||
targets.push(target);
|
targets.push(target);
|
||||||
}
|
}
|
||||||
let default_target =
|
let default_target = require_target(default, &context.value_stack, &context.frame_stack);
|
||||||
require_target(default, &context.value_stack, &context.frame_stack);
|
|
||||||
context.sink.emit_br_table(&targets, default_target);
|
context.sink.emit_br_table(&targets, default_target);
|
||||||
|
|
||||||
return Ok(Outcome::Unreachable);
|
return Ok(Outcome::Unreachable);
|
||||||
}
|
}
|
||||||
Return => {
|
Return => {
|
||||||
if let BlockType::Value(value_type) = context.return_type()? {
|
if let BlockType::Value(value_type) = context.return_type()? {
|
||||||
tee_value(
|
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let drop_keep =
|
let drop_keep = drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack);
|
||||||
drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack);
|
|
||||||
context.sink.emit(isa::Instruction::Return(drop_keep));
|
context.sink.emit(isa::Instruction::Return(drop_keep));
|
||||||
|
|
||||||
return Ok(Outcome::Unreachable);
|
return Ok(Outcome::Unreachable);
|
||||||
|
@ -1098,72 +1067,33 @@ impl FunctionReader {
|
||||||
struct Validator;
|
struct Validator;
|
||||||
|
|
||||||
impl Validator {
|
impl Validator {
|
||||||
fn validate_const(
|
fn validate_const(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
value_type: ValueType,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
push_value(&mut context.value_stack, value_type.into())?;
|
push_value(&mut context.value_stack, value_type.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_unop(
|
fn validate_unop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
value_type: ValueType,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
pop_value(
|
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
push_value(&mut context.value_stack, value_type.into())?;
|
push_value(&mut context.value_stack, value_type.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_binop(
|
fn validate_binop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
value_type: ValueType,
|
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
) -> Result<(), Error> {
|
|
||||||
pop_value(
|
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
pop_value(
|
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
push_value(&mut context.value_stack, value_type.into())?;
|
push_value(&mut context.value_stack, value_type.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_testop(
|
fn validate_testop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
value_type: ValueType,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
pop_value(
|
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_relop(
|
fn validate_relop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
value_type: ValueType,
|
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
) -> Result<(), Error> {
|
|
||||||
pop_value(
|
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
pop_value(
|
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1173,59 +1103,33 @@ impl Validator {
|
||||||
value_type1: ValueType,
|
value_type1: ValueType,
|
||||||
value_type2: ValueType,
|
value_type2: ValueType,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, value_type1.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type1.into(),
|
|
||||||
)?;
|
|
||||||
push_value(&mut context.value_stack, value_type2.into())?;
|
push_value(&mut context.value_stack, value_type2.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_drop(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
fn validate_drop(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
StackValueType::Any,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_select(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
fn validate_select(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||||
&mut context.value_stack,
|
let select_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?;
|
||||||
&context.frame_stack,
|
|
||||||
ValueType::I32.into(),
|
|
||||||
)?;
|
|
||||||
let select_type = pop_value(
|
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
StackValueType::Any,
|
|
||||||
)?;
|
|
||||||
pop_value(&mut context.value_stack, &context.frame_stack, select_type)?;
|
pop_value(&mut context.value_stack, &context.frame_stack, select_type)?;
|
||||||
push_value(&mut context.value_stack, select_type)?;
|
push_value(&mut context.value_stack, select_type)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_get_local(
|
fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
index: u32,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let local_type = require_local(&context.locals, index)?;
|
let local_type = require_local(&context.locals, index)?;
|
||||||
push_value(&mut context.value_stack, local_type.into())?;
|
push_value(&mut context.value_stack, local_type.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_set_local(
|
fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
index: u32,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let local_type = require_local(&context.locals, index)?;
|
let local_type = require_local(&context.locals, index)?;
|
||||||
let value_type = pop_value(
|
let value_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
StackValueType::Any,
|
|
||||||
)?;
|
|
||||||
if StackValueType::from(local_type) != value_type {
|
if StackValueType::from(local_type) != value_type {
|
||||||
return Err(Error(format!(
|
return Err(Error(format!(
|
||||||
"Trying to update local {} of type {:?} with value of type {:?}",
|
"Trying to update local {} of type {:?} with value of type {:?}",
|
||||||
|
@ -1235,23 +1139,13 @@ impl Validator {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_tee_local(
|
fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
index: u32,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let local_type = require_local(&context.locals, index)?;
|
let local_type = require_local(&context.locals, index)?;
|
||||||
tee_value(
|
tee_value(&mut context.value_stack, &context.frame_stack, local_type.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
local_type.into(),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_get_global(
|
fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
index: u32,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let global_type: StackValueType = {
|
let global_type: StackValueType = {
|
||||||
let global = context.module.require_global(index, None)?;
|
let global = context.module.require_global(index, None)?;
|
||||||
global.content_type().into()
|
global.content_type().into()
|
||||||
|
@ -1260,19 +1154,12 @@ impl Validator {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_set_global(
|
fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
index: u32,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let global_type: StackValueType = {
|
let global_type: StackValueType = {
|
||||||
let global = context.module.require_global(index, Some(true))?;
|
let global = context.module.require_global(index, Some(true))?;
|
||||||
global.content_type().into()
|
global.content_type().into()
|
||||||
};
|
};
|
||||||
let value_type = pop_value(
|
let value_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
StackValueType::Any,
|
|
||||||
)?;
|
|
||||||
if global_type != value_type {
|
if global_type != value_type {
|
||||||
return Err(Error(format!(
|
return Err(Error(format!(
|
||||||
"Trying to update global {} of type {:?} with value of type {:?}",
|
"Trying to update global {} of type {:?} with value of type {:?}",
|
||||||
|
@ -1295,11 +1182,7 @@ impl Validator {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
ValueType::I32.into(),
|
|
||||||
)?;
|
|
||||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||||
push_value(&mut context.value_stack, value_type.into())?;
|
push_value(&mut context.value_stack, value_type.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1319,16 +1202,8 @@ impl Validator {
|
||||||
}
|
}
|
||||||
|
|
||||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
&mut context.value_stack,
|
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
pop_value(
|
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
ValueType::I32.into(),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1339,22 +1214,14 @@ impl Validator {
|
||||||
};
|
};
|
||||||
if !frame_type.is_loop() {
|
if !frame_type.is_loop() {
|
||||||
if let BlockType::Value(value_type) = frame_block_type {
|
if let BlockType::Value(value_type) = frame_block_type {
|
||||||
tee_value(
|
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_br_if(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> {
|
fn validate_br_if(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> {
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
ValueType::I32.into(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (frame_type, frame_block_type) = {
|
let (frame_type, frame_block_type) = {
|
||||||
let frame = require_label(depth, &context.frame_stack)?;
|
let frame = require_label(depth, &context.frame_stack)?;
|
||||||
|
@ -1362,21 +1229,13 @@ impl Validator {
|
||||||
};
|
};
|
||||||
if !frame_type.is_loop() {
|
if !frame_type.is_loop() {
|
||||||
if let BlockType::Value(value_type) = frame_block_type {
|
if let BlockType::Value(value_type) = frame_block_type {
|
||||||
tee_value(
|
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_br_table(
|
fn validate_br_table(context: &mut FunctionValidationContext, table: &[u32], default: u32) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
table: &[u32],
|
|
||||||
default: u32,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let required_block_type: BlockType = {
|
let required_block_type: BlockType = {
|
||||||
let default_block = require_label(default, &context.frame_stack)?;
|
let default_block = require_label(default, &context.frame_stack)?;
|
||||||
let required_block_type = if !default_block.frame_type.is_loop() {
|
let required_block_type = if !default_block.frame_type.is_loop() {
|
||||||
|
@ -1402,17 +1261,9 @@ impl Validator {
|
||||||
required_block_type
|
required_block_type
|
||||||
};
|
};
|
||||||
|
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
ValueType::I32.into(),
|
|
||||||
)?;
|
|
||||||
if let BlockType::Value(value_type) = required_block_type {
|
if let BlockType::Value(value_type) = required_block_type {
|
||||||
tee_value(
|
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
value_type.into(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1421,11 +1272,7 @@ impl Validator {
|
||||||
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> {
|
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> {
|
||||||
let (argument_types, return_type) = context.module.require_function(idx)?;
|
let (argument_types, return_type) = context.module.require_function(idx)?;
|
||||||
for argument_type in argument_types.iter().rev() {
|
for argument_type in argument_types.iter().rev() {
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, (*argument_type).into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
(*argument_type).into(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
if let BlockType::Value(value_type) = return_type {
|
if let BlockType::Value(value_type) = return_type {
|
||||||
push_value(&mut context.value_stack, value_type.into())?;
|
push_value(&mut context.value_stack, value_type.into())?;
|
||||||
|
@ -1433,10 +1280,7 @@ impl Validator {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_call_indirect(
|
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> {
|
||||||
context: &mut FunctionValidationContext,
|
|
||||||
idx: u32,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
{
|
{
|
||||||
let table = context.module.require_table(DEFAULT_TABLE_INDEX)?;
|
let table = context.module.require_table(DEFAULT_TABLE_INDEX)?;
|
||||||
if table.elem_type() != TableElementType::AnyFunc {
|
if table.elem_type() != TableElementType::AnyFunc {
|
||||||
|
@ -1448,18 +1292,10 @@ impl Validator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
ValueType::I32.into(),
|
|
||||||
)?;
|
|
||||||
let (argument_types, return_type) = context.module.require_function_type(idx)?;
|
let (argument_types, return_type) = context.module.require_function_type(idx)?;
|
||||||
for argument_type in argument_types.iter().rev() {
|
for argument_type in argument_types.iter().rev() {
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, (*argument_type).into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
(*argument_type).into(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
if let BlockType::Value(value_type) = return_type {
|
if let BlockType::Value(value_type) = return_type {
|
||||||
push_value(&mut context.value_stack, value_type.into())?;
|
push_value(&mut context.value_stack, value_type.into())?;
|
||||||
|
@ -1475,11 +1311,7 @@ impl Validator {
|
||||||
|
|
||||||
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
||||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||||
pop_value(
|
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||||
&mut context.value_stack,
|
|
||||||
&context.frame_stack,
|
|
||||||
ValueType::I32.into(),
|
|
||||||
)?;
|
|
||||||
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1543,10 +1375,7 @@ fn make_top_frame_polymorphic(
|
||||||
frame.polymorphic_stack = true;
|
frame.polymorphic_stack = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_value(
|
fn push_value(value_stack: &mut StackWithLimit<StackValueType>, value_type: StackValueType) -> Result<(), Error> {
|
||||||
value_stack: &mut StackWithLimit<StackValueType>,
|
|
||||||
value_type: StackValueType,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
Ok(value_stack.push(value_type.into())?)
|
Ok(value_stack.push(value_type.into())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1564,19 +1393,14 @@ fn pop_value(
|
||||||
let actual_value = if stack_is_empty && is_stack_polymorphic {
|
let actual_value = if stack_is_empty && is_stack_polymorphic {
|
||||||
StackValueType::Any
|
StackValueType::Any
|
||||||
} else {
|
} else {
|
||||||
let value_stack_min = frame_stack
|
let value_stack_min = frame_stack.top().expect("at least 1 topmost block").value_stack_len;
|
||||||
.top()
|
|
||||||
.expect("at least 1 topmost block")
|
|
||||||
.value_stack_len;
|
|
||||||
if value_stack.len() <= value_stack_min {
|
if value_stack.len() <= value_stack_min {
|
||||||
return Err(Error("Trying to access parent frame stack values.".into()));
|
return Err(Error("Trying to access parent frame stack values.".into()));
|
||||||
}
|
}
|
||||||
value_stack.pop()?
|
value_stack.pop()?
|
||||||
};
|
};
|
||||||
match actual_value {
|
match actual_value {
|
||||||
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => {
|
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(actual_value),
|
||||||
Ok(actual_value)
|
|
||||||
}
|
|
||||||
StackValueType::Any => Ok(actual_value),
|
StackValueType::Any => Ok(actual_value),
|
||||||
stack_value_type @ _ => Err(Error(format!(
|
stack_value_type @ _ => Err(Error(format!(
|
||||||
"Expected value of type {:?} on top of stack. Got {:?}",
|
"Expected value of type {:?} on top of stack. Got {:?}",
|
||||||
|
@ -1623,11 +1447,7 @@ fn pop_label(
|
||||||
match block_type {
|
match block_type {
|
||||||
BlockType::NoResult => (),
|
BlockType::NoResult => (),
|
||||||
BlockType::Value(required_value_type) => {
|
BlockType::Value(required_value_type) => {
|
||||||
let _ = pop_value(
|
let _ = pop_value(value_stack, frame_stack, StackValueType::Specific(required_value_type))?;
|
||||||
value_stack,
|
|
||||||
frame_stack,
|
|
||||||
StackValueType::Specific(required_value_type),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1649,10 +1469,7 @@ fn top_label(frame_stack: &StackWithLimit<BlockFrame>) -> &BlockFrame {
|
||||||
.expect("this function can't be called with empty frame stack")
|
.expect("this function can't be called with empty frame stack")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_label(
|
fn require_label(depth: u32, frame_stack: &StackWithLimit<BlockFrame>) -> Result<&BlockFrame, Error> {
|
||||||
depth: u32,
|
|
||||||
frame_stack: &StackWithLimit<BlockFrame>,
|
|
||||||
) -> Result<&BlockFrame, Error> {
|
|
||||||
Ok(frame_stack.get(depth as usize)?)
|
Ok(frame_stack.get(depth as usize)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1662,8 +1479,7 @@ fn require_target(
|
||||||
frame_stack: &StackWithLimit<BlockFrame>,
|
frame_stack: &StackWithLimit<BlockFrame>,
|
||||||
) -> Target {
|
) -> Target {
|
||||||
let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack;
|
let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack;
|
||||||
let frame =
|
let frame = require_label(depth, frame_stack).expect("require_target called with a bogus depth");
|
||||||
require_label(depth, frame_stack).expect("require_target called with a bogus depth");
|
|
||||||
|
|
||||||
// Find out how many values we need to keep (copy to the new stack location after the drop).
|
// Find out how many values we need to keep (copy to the new stack location after the drop).
|
||||||
let keep: isa::Keep = match (frame.frame_type, frame.block_type) {
|
let keep: isa::Keep = match (frame.frame_type, frame.block_type) {
|
||||||
|
@ -1727,11 +1543,7 @@ fn require_local(locals: &Locals, idx: u32) -> Result<ValueType, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See stack layout definition in mod isa.
|
/// See stack layout definition in mod isa.
|
||||||
fn relative_local_depth(
|
fn relative_local_depth(idx: u32, locals: &Locals, value_stack: &StackWithLimit<StackValueType>) -> Result<u32, Error> {
|
||||||
idx: u32,
|
|
||||||
locals: &Locals,
|
|
||||||
value_stack: &StackWithLimit<StackValueType>,
|
|
||||||
) -> Result<u32, Error> {
|
|
||||||
let value_stack_height = value_stack.len() as u32;
|
let value_stack_height = value_stack.len() as u32;
|
||||||
let locals_and_params_count = locals.count();
|
let locals_and_params_count = locals.count();
|
||||||
|
|
||||||
|
@ -1779,11 +1591,7 @@ impl Sink {
|
||||||
self.ins.current_pc()
|
self.ins.current_pc()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pc_or_placeholder<F: FnOnce() -> isa::Reloc>(
|
fn pc_or_placeholder<F: FnOnce() -> isa::Reloc>(&mut self, label: LabelId, reloc_creator: F) -> u32 {
|
||||||
&mut self,
|
|
||||||
label: LabelId,
|
|
||||||
reloc_creator: F,
|
|
||||||
) -> u32 {
|
|
||||||
match self.labels[label.0] {
|
match self.labels[label.0] {
|
||||||
(Label::Resolved(dst_pc), _) => dst_pc,
|
(Label::Resolved(dst_pc), _) => dst_pc,
|
||||||
(Label::NotResolved, ref mut unresolved) => {
|
(Label::NotResolved, ref mut unresolved) => {
|
||||||
|
@ -1832,17 +1640,14 @@ impl Sink {
|
||||||
|
|
||||||
let pc = self.cur_pc();
|
let pc = self.cur_pc();
|
||||||
let mut isa_targets = Vec::new();
|
let mut isa_targets = Vec::new();
|
||||||
for (idx, &Target { label, drop_keep }) in
|
for (idx, &Target { label, drop_keep }) in targets.iter().chain(iter::once(&default)).enumerate() {
|
||||||
targets.iter().chain(iter::once(&default)).enumerate()
|
|
||||||
{
|
|
||||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx });
|
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx });
|
||||||
isa_targets.push(isa::Target {
|
isa_targets.push(isa::Target {
|
||||||
dst_pc,
|
dst_pc,
|
||||||
drop_keep: drop_keep.into(),
|
drop_keep: drop_keep.into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.ins
|
self.ins.push(isa::Instruction::BrTable(isa_targets.into_boxed_slice()));
|
||||||
.push(isa::Instruction::BrTable(isa_targets.into_boxed_slice()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new unresolved label.
|
/// Create a new unresolved label.
|
||||||
|
@ -1879,12 +1684,10 @@ impl Sink {
|
||||||
// At this moment all labels should be resolved.
|
// At this moment all labels should be resolved.
|
||||||
assert!(
|
assert!(
|
||||||
{
|
{
|
||||||
self.labels
|
self.labels.iter().all(|(state, unresolved)| match (state, unresolved) {
|
||||||
.iter()
|
(Label::Resolved(_), unresolved) if unresolved.is_empty() => true,
|
||||||
.all(|(state, unresolved)| match (state, unresolved) {
|
_ => false,
|
||||||
(Label::Resolved(_), unresolved) if unresolved.is_empty() => true,
|
})
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
"there are unresolved labels left: {:?}",
|
"there are unresolved labels left: {:?}",
|
||||||
self.labels
|
self.labels
|
||||||
|
|
|
@ -15,8 +15,8 @@ use common::stack;
|
||||||
use isa;
|
use isa;
|
||||||
use memory_units::Pages;
|
use memory_units::Pages;
|
||||||
use parity_wasm::elements::{
|
use parity_wasm::elements::{
|
||||||
BlockType, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, MemoryType,
|
BlockType, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, MemoryType, Module, ResizableLimits,
|
||||||
Module, ResizableLimits, TableType, Type, ValueType,
|
TableType, Type, ValueType,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
|
@ -194,11 +194,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fill elements with imported values.
|
// Fill elements with imported values.
|
||||||
for import_entry in module
|
for import_entry in module.import_section().map(|i| i.entries()).unwrap_or_default() {
|
||||||
.import_section()
|
|
||||||
.map(|i| i.entries())
|
|
||||||
.unwrap_or_default()
|
|
||||||
{
|
|
||||||
match *import_entry.external() {
|
match *import_entry.external() {
|
||||||
External::Function(idx) => context_builder.push_func_type_index(idx),
|
External::Function(idx) => context_builder.push_func_type_index(idx),
|
||||||
External::Table(ref table) => context_builder.push_table(table.clone()),
|
External::Table(ref table) => context_builder.push_table(table.clone()),
|
||||||
|
@ -237,10 +233,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
||||||
|
|
||||||
let context = context_builder.build();
|
let context = context_builder.build();
|
||||||
|
|
||||||
let function_section_len = module
|
let function_section_len = module.function_section().map(|s| s.entries().len()).unwrap_or(0);
|
||||||
.function_section()
|
|
||||||
.map(|s| s.entries().len())
|
|
||||||
.unwrap_or(0);
|
|
||||||
let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0);
|
let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0);
|
||||||
if function_section_len != code_section_len {
|
if function_section_len != code_section_len {
|
||||||
return Err(Error(format!(
|
return Err(Error(format!(
|
||||||
|
@ -252,9 +245,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
||||||
// 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
|
let function_section = module.function_section().expect("function_section_len != 0; qed");
|
||||||
.function_section()
|
|
||||||
.expect("function_section_len != 0; qed");
|
|
||||||
let code_section = module
|
let code_section = module
|
||||||
.code_section()
|
.code_section()
|
||||||
.expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
.expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
||||||
|
@ -264,14 +255,10 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
||||||
.bodies()
|
.bodies()
|
||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
.ok_or(Error(format!("Missing body for function {}", index)))?;
|
.ok_or(Error(format!("Missing body for function {}", index)))?;
|
||||||
let code =
|
let code = FunctionReader::read_function(&context, function, function_body).map_err(|e| {
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,9 +267,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
||||||
if let Some(start_fn_idx) = module.start_section() {
|
if let Some(start_fn_idx) = module.start_section() {
|
||||||
let (params, return_ty) = context.require_function(start_fn_idx)?;
|
let (params, return_ty) = context.require_function(start_fn_idx)?;
|
||||||
if return_ty != BlockType::NoResult || params.len() != 0 {
|
if return_ty != BlockType::NoResult || params.len() != 0 {
|
||||||
return Err(Error(
|
return Err(Error("start function expected to have type [] -> []".into()));
|
||||||
"start function expected to have type [] -> []".into(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,10 +306,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
||||||
}
|
}
|
||||||
External::Global(ref global_type) => {
|
External::Global(ref global_type) => {
|
||||||
if global_type.is_mutable() {
|
if global_type.is_mutable() {
|
||||||
return Err(Error(format!(
|
return Err(Error(format!("trying to import mutable global {}", import.field())));
|
||||||
"trying to import mutable global {}",
|
|
||||||
import.field()
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
External::Memory(ref memory_type) => {
|
External::Memory(ref memory_type) => {
|
||||||
|
@ -423,9 +405,7 @@ fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) ->
|
||||||
fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<ValueType, Error> {
|
fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<ValueType, Error> {
|
||||||
let code = init_expr.code();
|
let code = init_expr.code();
|
||||||
if code.len() != 2 {
|
if code.len() != 2 {
|
||||||
return Err(Error(
|
return Err(Error("Init expression should always be with length 2".into()));
|
||||||
"Init expression should always be with length 2".into(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let expr_ty: ValueType = match code[0] {
|
let expr_ty: ValueType = match code[0] {
|
||||||
Instruction::I32Const(_) => ValueType::I32,
|
Instruction::I32Const(_) => ValueType::I32,
|
||||||
|
@ -439,12 +419,7 @@ fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<Value
|
||||||
}
|
}
|
||||||
target_global.content_type()
|
target_global.content_type()
|
||||||
}
|
}
|
||||||
None => {
|
None => return Err(Error(format!("Global {} doesn't exists or not yet defined", idx))),
|
||||||
return Err(Error(format!(
|
|
||||||
"Global {} doesn't exists or not yet defined",
|
|
||||||
idx
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => return Err(Error("Non constant opcode in init expr".into())),
|
_ => return Err(Error("Non constant opcode in init expr".into())),
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,8 @@ use super::{validate_module, ValidatedModule};
|
||||||
use isa;
|
use isa;
|
||||||
use parity_wasm::builder::module;
|
use parity_wasm::builder::module;
|
||||||
use parity_wasm::elements::{
|
use parity_wasm::elements::{
|
||||||
deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr,
|
deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr, Instruction, Instructions,
|
||||||
Instruction, Instructions, MemoryType, Module, TableType, ValueType,
|
MemoryType, Module, TableType, ValueType,
|
||||||
};
|
};
|
||||||
use wabt;
|
use wabt;
|
||||||
|
|
||||||
|
@ -39,12 +39,7 @@ fn limits() {
|
||||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||||
|
|
||||||
// defined memory
|
// defined memory
|
||||||
let m = module()
|
let m = module().memory().with_min(min).with_max(max).build().build();
|
||||||
.memory()
|
|
||||||
.with_min(min)
|
|
||||||
.with_max(max)
|
|
||||||
.build()
|
|
||||||
.build();
|
|
||||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||||
|
|
||||||
// imported table
|
// imported table
|
||||||
|
@ -185,10 +180,8 @@ fn funcs() {
|
||||||
.i32()
|
.i32()
|
||||||
.build()
|
.build()
|
||||||
.body()
|
.body()
|
||||||
.with_instructions(Instructions::new(vec![
|
.with_instructions(Instructions::new(vec![Instruction::Call(1), Instruction::End]))
|
||||||
Instruction::Call(1),
|
.build()
|
||||||
Instruction::End,
|
|
||||||
])).build()
|
|
||||||
.build()
|
.build()
|
||||||
.function()
|
.function()
|
||||||
.signature()
|
.signature()
|
||||||
|
@ -196,10 +189,8 @@ fn funcs() {
|
||||||
.i32()
|
.i32()
|
||||||
.build()
|
.build()
|
||||||
.body()
|
.body()
|
||||||
.with_instructions(Instructions::new(vec![
|
.with_instructions(Instructions::new(vec![Instruction::Call(0), Instruction::End]))
|
||||||
Instruction::Call(0),
|
.build()
|
||||||
Instruction::End,
|
|
||||||
])).build()
|
|
||||||
.build()
|
.build()
|
||||||
.build();
|
.build();
|
||||||
assert!(validate_module(m).is_ok());
|
assert!(validate_module(m).is_ok());
|
||||||
|
|
|
@ -590,10 +590,7 @@ impl LittleEndianConvert for u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
fn from_little_endian(buffer: &[u8]) -> Result<Self, Error> {
|
||||||
buffer
|
buffer.get(0).cloned().ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
||||||
.get(0)
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| Error::InvalidLittleEndianBuffer)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,9 @@ 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::{
|
use wasmi::{
|
||||||
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance,
|
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef,
|
||||||
GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
|
ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleImportResolver,
|
||||||
ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature,
|
ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap,
|
||||||
TableDescriptor, TableInstance, TableRef, Trap,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
|
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
|
||||||
|
@ -64,11 +63,7 @@ impl SpecModule {
|
||||||
const PRINT_FUNC_INDEX: usize = 0;
|
const PRINT_FUNC_INDEX: usize = 0;
|
||||||
|
|
||||||
impl Externals for SpecModule {
|
impl Externals for SpecModule {
|
||||||
fn invoke_index(
|
fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
&mut self,
|
|
||||||
index: usize,
|
|
||||||
args: RuntimeArgs,
|
|
||||||
) -> Result<Option<RuntimeValue>, Trap> {
|
|
||||||
match index {
|
match index {
|
||||||
PRINT_FUNC_INDEX => {
|
PRINT_FUNC_INDEX => {
|
||||||
println!("print: {:?}", args);
|
println!("print: {:?}", args);
|
||||||
|
@ -80,11 +75,7 @@ impl Externals for SpecModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleImportResolver for SpecModule {
|
impl ModuleImportResolver for SpecModule {
|
||||||
fn resolve_func(
|
fn resolve_func(&self, field_name: &str, func_type: &Signature) -> Result<FuncRef, InterpreterError> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
func_type: &Signature,
|
|
||||||
) -> Result<FuncRef, InterpreterError> {
|
|
||||||
let index = match field_name {
|
let index = match field_name {
|
||||||
"print" => PRINT_FUNC_INDEX,
|
"print" => PRINT_FUNC_INDEX,
|
||||||
"print_i32" => PRINT_FUNC_INDEX,
|
"print_i32" => PRINT_FUNC_INDEX,
|
||||||
|
@ -110,11 +101,7 @@ impl ModuleImportResolver for SpecModule {
|
||||||
return Ok(func);
|
return Ok(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_global(
|
fn resolve_global(&self, field_name: &str, _global_type: &GlobalDescriptor) -> Result<GlobalRef, InterpreterError> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_global_type: &GlobalDescriptor,
|
|
||||||
) -> Result<GlobalRef, InterpreterError> {
|
|
||||||
match field_name {
|
match field_name {
|
||||||
"global_i32" => Ok(self.global_i32.clone()),
|
"global_i32" => Ok(self.global_i32.clone()),
|
||||||
"global_f32" => Ok(self.global_f32.clone()),
|
"global_f32" => Ok(self.global_f32.clone()),
|
||||||
|
@ -126,11 +113,7 @@ impl ModuleImportResolver for SpecModule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_memory(
|
fn resolve_memory(&self, field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, InterpreterError> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_memory_type: &MemoryDescriptor,
|
|
||||||
) -> Result<MemoryRef, InterpreterError> {
|
|
||||||
if field_name == "memory" {
|
if field_name == "memory" {
|
||||||
return Ok(self.memory.clone());
|
return Ok(self.memory.clone());
|
||||||
}
|
}
|
||||||
|
@ -141,11 +124,7 @@ impl ModuleImportResolver for SpecModule {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_table(
|
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, InterpreterError> {
|
||||||
&self,
|
|
||||||
field_name: &str,
|
|
||||||
_table_type: &TableDescriptor,
|
|
||||||
) -> Result<TableRef, InterpreterError> {
|
|
||||||
if field_name == "table" {
|
if field_name == "table" {
|
||||||
return Ok(self.table.clone());
|
return Ok(self.table.clone());
|
||||||
}
|
}
|
||||||
|
@ -184,9 +163,10 @@ impl SpecDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
|
fn module(&self, name: &str) -> Result<ModuleRef, InterpreterError> {
|
||||||
self.instances.get(name).cloned().ok_or_else(|| {
|
self.instances
|
||||||
InterpreterError::Instantiation(format!("Module not registered {}", name))
|
.get(name)
|
||||||
})
|
.cloned()
|
||||||
|
.ok_or_else(|| InterpreterError::Instantiation(format!("Module not registered {}", name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
|
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
|
||||||
|
@ -210,8 +190,7 @@ impl ImportResolver for SpecDriver {
|
||||||
if module_name == "spectest" {
|
if module_name == "spectest" {
|
||||||
self.spec_module.resolve_func(field_name, func_type)
|
self.spec_module.resolve_func(field_name, func_type)
|
||||||
} else {
|
} else {
|
||||||
self.module(module_name)?
|
self.module(module_name)?.resolve_func(field_name, func_type)
|
||||||
.resolve_func(field_name, func_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,8 +203,7 @@ impl ImportResolver for SpecDriver {
|
||||||
if module_name == "spectest" {
|
if module_name == "spectest" {
|
||||||
self.spec_module.resolve_global(field_name, global_type)
|
self.spec_module.resolve_global(field_name, global_type)
|
||||||
} else {
|
} else {
|
||||||
self.module(module_name)?
|
self.module(module_name)?.resolve_global(field_name, global_type)
|
||||||
.resolve_global(field_name, global_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,8 +216,7 @@ impl ImportResolver for SpecDriver {
|
||||||
if module_name == "spectest" {
|
if module_name == "spectest" {
|
||||||
self.spec_module.resolve_memory(field_name, memory_type)
|
self.spec_module.resolve_memory(field_name, memory_type)
|
||||||
} else {
|
} else {
|
||||||
self.module(module_name)?
|
self.module(module_name)?.resolve_memory(field_name, memory_type)
|
||||||
.resolve_memory(field_name, memory_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,8 +229,7 @@ impl ImportResolver for SpecDriver {
|
||||||
if module_name == "spectest" {
|
if module_name == "spectest" {
|
||||||
self.spec_module.resolve_table(field_name, table_type)
|
self.spec_module.resolve_table(field_name, table_type)
|
||||||
} else {
|
} else {
|
||||||
self.module(module_name)?
|
self.module(module_name)?.resolve_table(field_name, table_type)
|
||||||
.resolve_table(field_name, table_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,11 +247,7 @@ fn try_load(wasm: &[u8], spec_driver: &mut SpecDriver) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_module(
|
fn load_module(wasm: &[u8], name: &Option<String>, spec_driver: &mut SpecDriver) -> Result<ModuleRef, Error> {
|
||||||
wasm: &[u8],
|
|
||||||
name: &Option<String>,
|
|
||||||
spec_driver: &mut SpecDriver,
|
|
||||||
) -> Result<ModuleRef, Error> {
|
|
||||||
let module = try_load_module(wasm)?;
|
let module = try_load_module(wasm)?;
|
||||||
let instance = ModuleInstance::new(&module, spec_driver)
|
let instance = ModuleInstance::new(&module, spec_driver)
|
||||||
.map_err(|e| Error::Load(e.to_string()))?
|
.map_err(|e| Error::Load(e.to_string()))?
|
||||||
|
@ -288,10 +260,7 @@ fn load_module(
|
||||||
Ok(instance)
|
Ok(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_action(
|
fn run_action(program: &mut SpecDriver, action: &Action<u32, u64>) -> Result<Option<RuntimeValue>, InterpreterError> {
|
||||||
program: &mut SpecDriver,
|
|
||||||
action: &Action<u32, u64>,
|
|
||||||
) -> Result<Option<RuntimeValue>, InterpreterError> {
|
|
||||||
match *action {
|
match *action {
|
||||||
Action::Invoke {
|
Action::Invoke {
|
||||||
ref module,
|
ref module,
|
||||||
|
@ -300,37 +269,22 @@ fn run_action(
|
||||||
} => {
|
} => {
|
||||||
let module = program
|
let module = program
|
||||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||||
.expect(&format!(
|
.expect(&format!("Expected program to have loaded module {:?}", module));
|
||||||
"Expected program to have loaded module {:?}",
|
let vec_args = args.iter().cloned().map(spec_to_runtime_value).collect::<Vec<_>>();
|
||||||
module
|
|
||||||
));
|
|
||||||
let vec_args = args
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(spec_to_runtime_value)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
module.invoke_export(field, &vec_args, program.spec_module())
|
module.invoke_export(field, &vec_args, program.spec_module())
|
||||||
}
|
}
|
||||||
Action::Get {
|
Action::Get {
|
||||||
ref module,
|
ref module, ref field, ..
|
||||||
ref field,
|
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
let module = program
|
let module = program
|
||||||
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
.module_or_last(module.as_ref().map(|x| x.as_ref()))
|
||||||
.expect(&format!(
|
.expect(&format!("Expected program to have loaded module {:?}", module));
|
||||||
"Expected program to have loaded module {:?}",
|
|
||||||
module
|
|
||||||
));
|
|
||||||
let global = module
|
let global = module
|
||||||
.export_by_name(&field)
|
.export_by_name(&field)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| InterpreterError::Global(format!("Expected to have export with name {}", field)))?
|
||||||
InterpreterError::Global(format!("Expected to have export with name {}", field))
|
.as_global()
|
||||||
})?.as_global()
|
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| InterpreterError::Global(format!("Expected export {} to be a global", field)))?;
|
||||||
InterpreterError::Global(format!("Expected export {} to be a global", field))
|
|
||||||
})?;
|
|
||||||
Ok(Some(global.get()))
|
Ok(Some(global.get()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,12 +302,10 @@ 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
|
spec_file.read_to_end(&mut spec_source).expect("Can't read 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))
|
let mut parser =
|
||||||
.expect("Can't read spec script");
|
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()? {
|
||||||
|
@ -377,22 +329,15 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
CommandKind::Module { name, module, .. } => {
|
CommandKind::Module { name, module, .. } => {
|
||||||
load_module(&module.into_vec(), &name, &mut spec_driver)
|
load_module(&module.into_vec(), &name, &mut spec_driver).expect("Failed to load module");
|
||||||
.expect("Failed to load module");
|
|
||||||
}
|
}
|
||||||
CommandKind::AssertReturn { action, expected } => {
|
CommandKind::AssertReturn { action, expected } => {
|
||||||
let result = run_action(&mut spec_driver, &action);
|
let result = run_action(&mut spec_driver, &action);
|
||||||
match result {
|
match result {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
let spec_expected = expected
|
let spec_expected = expected.iter().cloned().map(spec_to_runtime_value).collect::<Vec<_>>();
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(spec_to_runtime_value)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
|
let actual_result = result.into_iter().collect::<Vec<RuntimeValue>>();
|
||||||
for (actual_result, spec_expected) in
|
for (actual_result, spec_expected) in actual_result.iter().zip(spec_expected.iter()) {
|
||||||
actual_result.iter().zip(spec_expected.iter())
|
|
||||||
{
|
|
||||||
assert_eq!(actual_result.value_type(), spec_expected.value_type());
|
assert_eq!(actual_result.value_type(), spec_expected.value_type());
|
||||||
// f32::NAN != f32::NAN
|
// f32::NAN != f32::NAN
|
||||||
match spec_expected {
|
match spec_expected {
|
||||||
|
@ -413,8 +358,7 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandKind::AssertReturnCanonicalNan { action }
|
CommandKind::AssertReturnCanonicalNan { action } | CommandKind::AssertReturnArithmeticNan { action } => {
|
||||||
| CommandKind::AssertReturnArithmeticNan { action } => {
|
|
||||||
let result = run_action(&mut spec_driver, &action);
|
let result = run_action(&mut spec_driver, &action);
|
||||||
match result {
|
match result {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
|
@ -426,9 +370,7 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -448,10 +390,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) => {
|
Ok(result) => {
|
||||||
panic!(
|
panic!("Expected action to result in a trap, got result: {:?}", result);
|
||||||
"Expected action to result in a trap, got result: {:?}",
|
|
||||||
result
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Err(_e) => {}
|
Err(_e) => {}
|
||||||
}
|
}
|
||||||
|
@ -465,12 +404,10 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
||||||
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, .. } => {
|
||||||
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
|
let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) {
|
||||||
Ok(module) => module,
|
Ok(module) => module,
|
||||||
|
|
Loading…
Reference in New Issue