run 'cargo fmt'
This commit is contained in:
parent
13f08b3f9e
commit
55ab18f355
|
@ -1,7 +1,6 @@
|
|||
use std::env;
|
||||
use std::process;
|
||||
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=./wasm-kernel/");
|
||||
|
||||
|
@ -23,9 +22,9 @@ fn main() {
|
|||
if !output.status.success() {
|
||||
let msg = format!(
|
||||
"status: {status}\nstdout: {stdout}\nstderr: {stderr}\n",
|
||||
status=output.status,
|
||||
stdout=String::from_utf8_lossy(&output.stdout),
|
||||
stderr=String::from_utf8_lossy(&output.stderr),
|
||||
status = output.status,
|
||||
stdout = String::from_utf8_lossy(&output.stdout),
|
||||
stderr = String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
panic!("{}", msg);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
use std::fs::File;
|
||||
use wasmi::{ModuleInstance, NopExternals, RuntimeValue, ImportsBuilder, Module};
|
||||
use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue};
|
||||
|
||||
fn load_from_file(filename: &str) -> Module {
|
||||
use std::io::prelude::*;
|
||||
|
@ -15,30 +15,33 @@ fn load_from_file(filename: &str) -> Module {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<_> = args().collect();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} <wasm file> <arg>", args[0]);
|
||||
println!(" wasm file should contain exported `_call` function with single I32 argument");
|
||||
return;
|
||||
}
|
||||
let args: Vec<_> = args().collect();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} <wasm file> <arg>", args[0]);
|
||||
println!(" wasm file should contain exported `_call` function with single I32 argument");
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we load module using dedicated for this purpose
|
||||
// `load_from_file` function (which works only with modules)
|
||||
let module = load_from_file(&args[1]);
|
||||
// Here we load module using dedicated for this purpose
|
||||
// `load_from_file` function (which works only with modules)
|
||||
let module = load_from_file(&args[1]);
|
||||
|
||||
// Intialize deserialized module. It adds module into It expects 3 parameters:
|
||||
// - a name for the module
|
||||
// - a module declaration
|
||||
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
|
||||
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
|
||||
let main = ModuleInstance::new(&module, &ImportsBuilder::default())
|
||||
.expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
// Intialize deserialized module. It adds module into It expects 3 parameters:
|
||||
// - a name for the module
|
||||
// - a module declaration
|
||||
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
|
||||
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
|
||||
let main = ModuleInstance::new(&module, &ImportsBuilder::default())
|
||||
.expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
|
||||
// The argument should be parsable as a valid integer
|
||||
let argument: i32 = args[2].parse().expect("Integer argument required");
|
||||
// The argument should be parsable as a valid integer
|
||||
let argument: i32 = args[2].parse().expect("Integer argument required");
|
||||
|
||||
// "_call" export of function to be executed with an i32 argument and prints the result of execution
|
||||
println!("Result: {:?}", main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals));
|
||||
// "_call" export of function to be executed with an i32 argument and prints the result of execution
|
||||
println!(
|
||||
"Result: {:?}",
|
||||
main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,83 +3,116 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
|
||||
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType};
|
||||
use wasmi::{RuntimeValue, ModuleInstance, NopExternals, ImportsBuilder};
|
||||
|
||||
use parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType};
|
||||
use wasmi::{ImportsBuilder, ModuleInstance, NopExternals, RuntimeValue};
|
||||
|
||||
fn main() {
|
||||
let args: Vec<_> = args().collect();
|
||||
if args.len() < 3 {
|
||||
println!("Usage: {} <wasm file> <exported func> [<arg>...]", args[0]);
|
||||
return;
|
||||
}
|
||||
let func_name = &args[2];
|
||||
let (_, program_args) = args.split_at(3);
|
||||
let args: Vec<_> = args().collect();
|
||||
if args.len() < 3 {
|
||||
println!("Usage: {} <wasm file> <exported func> [<arg>...]", args[0]);
|
||||
return;
|
||||
}
|
||||
let func_name = &args[2];
|
||||
let (_, program_args) = args.split_at(3);
|
||||
|
||||
let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized");
|
||||
let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized");
|
||||
|
||||
// Extracts call arguments from command-line arguments
|
||||
let args = {
|
||||
// Export section has an entry with a func_name with an index inside a module
|
||||
let export_section = module.export_section().expect("No export section found");
|
||||
// It's a section with function declarations (which are references to the type section entries)
|
||||
let function_section = module.function_section().expect("No function section found");
|
||||
// Type section stores function types which are referenced by function_section entries
|
||||
let type_section = module.type_section().expect("No type section found");
|
||||
// Extracts call arguments from command-line arguments
|
||||
let args = {
|
||||
// Export section has an entry with a func_name with an index inside a module
|
||||
let export_section = module.export_section().expect("No export section found");
|
||||
// It's a section with function declarations (which are references to the type section entries)
|
||||
let function_section = module
|
||||
.function_section()
|
||||
.expect("No function section found");
|
||||
// Type section stores function types which are referenced by function_section entries
|
||||
let type_section = module.type_section().expect("No type section found");
|
||||
|
||||
// Given function name used to find export section entry which contains
|
||||
// an `internal` field which points to the index in the function index space
|
||||
let found_entry = export_section.entries().iter()
|
||||
.find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name));
|
||||
// Given function name used to find export section entry which contains
|
||||
// an `internal` field which points to the index in the function index space
|
||||
let found_entry = export_section
|
||||
.entries()
|
||||
.iter()
|
||||
.find(|entry| func_name == entry.field())
|
||||
.expect(&format!("No export with name {} found", func_name));
|
||||
|
||||
// Function index in the function index space (internally-defined + imported)
|
||||
let function_index: usize = match found_entry.internal() {
|
||||
&Internal::Function(index) => index as usize,
|
||||
_ => panic!("Founded export is not a function"),
|
||||
};
|
||||
// Function index in the function index space (internally-defined + imported)
|
||||
let function_index: usize = match found_entry.internal() {
|
||||
&Internal::Function(index) => index as usize,
|
||||
_ => panic!("Founded export is not a function"),
|
||||
};
|
||||
|
||||
// We need to count import section entries (functions only!) to subtract it from function_index
|
||||
// and obtain the index within the function section
|
||||
let import_section_len: usize = match module.import_section() {
|
||||
Some(import) =>
|
||||
import.entries().iter().filter(|entry| match entry.external() {
|
||||
&External::Function(_) => true,
|
||||
_ => false,
|
||||
}).count(),
|
||||
None => 0,
|
||||
};
|
||||
// We need to count import section entries (functions only!) to subtract it from function_index
|
||||
// and obtain the index within the function section
|
||||
let import_section_len: usize = match module.import_section() {
|
||||
Some(import) => import
|
||||
.entries()
|
||||
.iter()
|
||||
.filter(|entry| match entry.external() {
|
||||
&External::Function(_) => true,
|
||||
_ => false,
|
||||
}).count(),
|
||||
None => 0,
|
||||
};
|
||||
|
||||
// Calculates a function index within module's function section
|
||||
let function_index_in_section = function_index - import_section_len;
|
||||
// Calculates a function index within module's function section
|
||||
let function_index_in_section = function_index - import_section_len;
|
||||
|
||||
// Getting a type reference from a function section entry
|
||||
let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize;
|
||||
// Getting a type reference from a function section entry
|
||||
let func_type_ref: usize =
|
||||
function_section.entries()[function_index_in_section].type_ref() as usize;
|
||||
|
||||
// Use the reference to get an actual function type
|
||||
let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
|
||||
&Type::Function(ref func_type) => func_type,
|
||||
};
|
||||
// Use the reference to get an actual function type
|
||||
let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
|
||||
&Type::Function(ref func_type) => func_type,
|
||||
};
|
||||
|
||||
// Parses arguments and constructs runtime values in correspondence of their types
|
||||
function_type.params().iter().enumerate().map(|(i, value)| match value {
|
||||
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
|
||||
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
|
||||
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i])).into()),
|
||||
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i])).into()),
|
||||
}).collect::<Vec<RuntimeValue>>()
|
||||
};
|
||||
// Parses arguments and constructs runtime values in correspondence of their types
|
||||
function_type
|
||||
.params()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, value)| match value {
|
||||
&ValueType::I32 => RuntimeValue::I32(
|
||||
program_args[i]
|
||||
.parse::<i32>()
|
||||
.expect(&format!("Can't parse arg #{} as i32", program_args[i])),
|
||||
),
|
||||
&ValueType::I64 => RuntimeValue::I64(
|
||||
program_args[i]
|
||||
.parse::<i64>()
|
||||
.expect(&format!("Can't parse arg #{} as i64", program_args[i])),
|
||||
),
|
||||
&ValueType::F32 => RuntimeValue::F32(
|
||||
program_args[i]
|
||||
.parse::<f32>()
|
||||
.expect(&format!("Can't parse arg #{} as f32", program_args[i]))
|
||||
.into(),
|
||||
),
|
||||
&ValueType::F64 => RuntimeValue::F64(
|
||||
program_args[i]
|
||||
.parse::<f64>()
|
||||
.expect(&format!("Can't parse arg #{} as f64", program_args[i]))
|
||||
.into(),
|
||||
),
|
||||
}).collect::<Vec<RuntimeValue>>()
|
||||
};
|
||||
|
||||
let loaded_module = wasmi::Module::from_parity_wasm_module(module).expect("Module to be valid");
|
||||
let loaded_module = wasmi::Module::from_parity_wasm_module(module).expect("Module to be valid");
|
||||
|
||||
// Intialize deserialized module. It adds module into It expects 3 parameters:
|
||||
// - a name for the module
|
||||
// - a module declaration
|
||||
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
|
||||
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
|
||||
let main = ModuleInstance::new(&loaded_module, &ImportsBuilder::default())
|
||||
.expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
// Intialize deserialized module. It adds module into It expects 3 parameters:
|
||||
// - a name for the module
|
||||
// - a module declaration
|
||||
// - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here
|
||||
// This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197
|
||||
let main = ModuleInstance::new(&loaded_module, &ImportsBuilder::default())
|
||||
.expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
|
||||
println!("Result: {:?}", main.invoke_export(func_name, &args, &mut NopExternals).expect(""));
|
||||
println!(
|
||||
"Result: {:?}",
|
||||
main.invoke_export(func_name, &args, &mut NopExternals)
|
||||
.expect("")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
extern crate wasmi;
|
||||
extern crate parity_wasm;
|
||||
extern crate wasmi;
|
||||
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use wasmi::{
|
||||
Error as InterpreterError, ModuleInstance, ModuleRef,
|
||||
Externals, RuntimeValue, FuncRef, ModuleImportResolver,
|
||||
FuncInstance, HostError, ImportsBuilder, Signature, ValueType,
|
||||
RuntimeArgs, Trap,
|
||||
Error as InterpreterError, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
|
||||
ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, Trap,
|
||||
ValueType,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -64,9 +63,7 @@ mod tictactoe {
|
|||
|
||||
impl Game {
|
||||
pub fn new() -> Game {
|
||||
Game {
|
||||
board: [None; 9],
|
||||
}
|
||||
Game { board: [None; 9] }
|
||||
}
|
||||
|
||||
pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
|
||||
|
@ -96,12 +93,10 @@ mod tictactoe {
|
|||
(0, 1, 2),
|
||||
(3, 4, 5),
|
||||
(6, 7, 8),
|
||||
|
||||
// Columns
|
||||
(0, 3, 6),
|
||||
(1, 4, 7),
|
||||
(2, 5, 8),
|
||||
|
||||
// Diagonals
|
||||
(0, 4, 8),
|
||||
(2, 4, 6),
|
||||
|
@ -161,7 +156,7 @@ impl<'a> Externals for Runtime<'a> {
|
|||
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
|
||||
Ok(Some(val.into()))
|
||||
}
|
||||
_ => panic!("unknown function index")
|
||||
_ => panic!("unknown function index"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -175,15 +170,20 @@ impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
|
|||
_signature: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
let func_ref = match field_name {
|
||||
"set" => {
|
||||
FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], None), SET_FUNC_INDEX)
|
||||
},
|
||||
"get" => FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], Some(ValueType::I32)), GET_FUNC_INDEX),
|
||||
_ => return Err(
|
||||
InterpreterError::Function(
|
||||
format!("host module doesn't export function with name {}", field_name)
|
||||
)
|
||||
)
|
||||
"set" => FuncInstance::alloc_host(
|
||||
Signature::new(&[ValueType::I32][..], None),
|
||||
SET_FUNC_INDEX,
|
||||
),
|
||||
"get" => FuncInstance::alloc_host(
|
||||
Signature::new(&[ValueType::I32][..], Some(ValueType::I32)),
|
||||
GET_FUNC_INDEX,
|
||||
),
|
||||
_ => {
|
||||
return Err(InterpreterError::Function(format!(
|
||||
"host module doesn't export function with name {}",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
};
|
||||
Ok(func_ref)
|
||||
}
|
||||
|
@ -201,8 +201,7 @@ fn instantiate(path: &str) -> Result<ModuleRef, Error> {
|
|||
let mut imports = ImportsBuilder::new();
|
||||
imports.push_resolver("env", &RuntimeModuleImportResolver);
|
||||
|
||||
let instance = ModuleInstance::new(&module, &imports)?
|
||||
.assert_no_start();
|
||||
let instance = ModuleInstance::new(&module, &imports)?.assert_no_start();
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@ extern crate wasmi;
|
|||
|
||||
use std::env::args;
|
||||
use std::fs::File;
|
||||
use wasmi::{
|
||||
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef,
|
||||
ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
|
||||
ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, Signature,
|
||||
TableDescriptor, TableInstance, TableRef};
|
||||
use wasmi::memory_units::*;
|
||||
use wasmi::{
|
||||
Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder,
|
||||
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance,
|
||||
NopExternals, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
|
||||
};
|
||||
|
||||
fn load_from_file(filename: &str) -> Module {
|
||||
use std::io::prelude::*;
|
||||
|
@ -76,6 +76,6 @@ fn main() {
|
|||
.with_resolver("asm2wasm", &ResolveAll)
|
||||
.with_resolver("spectest", &ResolveAll),
|
||||
).expect("Failed to instantiate module")
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
.run_start(&mut NopExternals)
|
||||
.expect("Failed to run start function in module");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
pub mod stack;
|
||||
|
||||
/// Index of default linear memory.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
||||
use core::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error(String);
|
||||
|
@ -23,18 +23,24 @@ impl error::Error for Error {
|
|||
|
||||
/// Stack with limit.
|
||||
#[derive(Debug)]
|
||||
pub struct StackWithLimit<T> where T: Clone {
|
||||
pub struct StackWithLimit<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
/// Stack values.
|
||||
values: Vec<T>,
|
||||
/// Stack limit (maximal stack len).
|
||||
limit: usize,
|
||||
}
|
||||
|
||||
impl<T> StackWithLimit<T> where T: Clone {
|
||||
impl<T> StackWithLimit<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
pub fn with_limit(limit: usize) -> Self {
|
||||
StackWithLimit {
|
||||
values: Vec::new(),
|
||||
limit: limit
|
||||
limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,10 +66,17 @@ impl<T> StackWithLimit<T> where T: Clone {
|
|||
|
||||
pub fn get(&self, index: usize) -> Result<&T, Error> {
|
||||
if index >= self.values.len() {
|
||||
return Err(Error(format!("trying to get value at position {} on stack of size {}", index, self.values.len())));
|
||||
return Err(Error(format!(
|
||||
"trying to get value at position {} on stack of size {}",
|
||||
index,
|
||||
self.values.len()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(self.values.get(self.values.len() - 1 - index).expect("checked couple of lines above"))
|
||||
Ok(self
|
||||
.values
|
||||
.get(self.values.len() - 1 - index)
|
||||
.expect("checked couple of lines above"))
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T) -> Result<(), Error> {
|
||||
|
|
74
src/func.rs
74
src/func.rs
|
@ -2,14 +2,14 @@
|
|||
use alloc::prelude::*;
|
||||
use alloc::rc::{Rc, Weak};
|
||||
use core::fmt;
|
||||
use parity_wasm::elements::Local;
|
||||
use {Trap, Signature};
|
||||
use host::Externals;
|
||||
use runner::{check_function_args, Interpreter, InterpreterState};
|
||||
use value::RuntimeValue;
|
||||
use types::ValueType;
|
||||
use module::ModuleInstance;
|
||||
use isa;
|
||||
use module::ModuleInstance;
|
||||
use parity_wasm::elements::Local;
|
||||
use runner::{check_function_args, Interpreter, InterpreterState};
|
||||
use types::ValueType;
|
||||
use value::RuntimeValue;
|
||||
use {Signature, Trap};
|
||||
|
||||
/// Reference to a function (See [`FuncInstance`] for details).
|
||||
///
|
||||
|
@ -58,17 +58,10 @@ pub(crate) enum FuncInstanceInternal {
|
|||
impl fmt::Debug for FuncInstance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.as_internal() {
|
||||
&FuncInstanceInternal::Internal {
|
||||
ref signature,
|
||||
..
|
||||
} => {
|
||||
&FuncInstanceInternal::Internal { ref signature, .. } => {
|
||||
// We can't write description of self.module here, because it generate
|
||||
// debug string for function instances and this will lead to infinite loop.
|
||||
write!(
|
||||
f,
|
||||
"Internal {{ signature={:?} }}",
|
||||
signature,
|
||||
)
|
||||
write!(f, "Internal {{ signature={:?} }}", signature,)
|
||||
}
|
||||
&FuncInstanceInternal::Host { ref signature, .. } => {
|
||||
write!(f, "Host {{ signature={:?} }}", signature)
|
||||
|
@ -186,15 +179,13 @@ impl FuncInstance {
|
|||
FuncInstanceInternal::Host {
|
||||
ref host_func_index,
|
||||
..
|
||||
} => {
|
||||
Ok(FuncInvocation {
|
||||
kind: FuncInvocationKind::Host {
|
||||
args,
|
||||
host_func_index: *host_func_index,
|
||||
finished: false,
|
||||
},
|
||||
})
|
||||
},
|
||||
} => Ok(FuncInvocation {
|
||||
kind: FuncInvocationKind::Host {
|
||||
args,
|
||||
host_func_index: *host_func_index,
|
||||
finished: false,
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +230,7 @@ enum FuncInvocationKind<'args> {
|
|||
Host {
|
||||
args: &'args [RuntimeValue],
|
||||
host_func_index: usize,
|
||||
finished: bool
|
||||
finished: bool,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -255,32 +246,37 @@ impl<'args> FuncInvocation<'args> {
|
|||
/// If the invocation is resumable, the expected return value type to be feed back in.
|
||||
pub fn resumable_value_type(&self) -> Option<ValueType> {
|
||||
match &self.kind {
|
||||
&FuncInvocationKind::Internal(ref interpreter) => {
|
||||
match interpreter.state() {
|
||||
&InterpreterState::Resumable(ref value_type) => value_type.clone(),
|
||||
_ => None,
|
||||
}
|
||||
&FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() {
|
||||
&InterpreterState::Resumable(ref value_type) => value_type.clone(),
|
||||
_ => None,
|
||||
},
|
||||
&FuncInvocationKind::Host { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the invocation execution.
|
||||
pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
pub fn start_execution<'externals, E: Externals + 'externals>(
|
||||
&mut self,
|
||||
externals: &'externals mut E,
|
||||
) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
match self.kind {
|
||||
FuncInvocationKind::Internal(ref mut interpreter) => {
|
||||
if interpreter.state() != &InterpreterState::Initialized {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
Ok(interpreter.start_execution(externals)?)
|
||||
},
|
||||
FuncInvocationKind::Host { ref args, ref mut finished, ref host_func_index } => {
|
||||
}
|
||||
FuncInvocationKind::Host {
|
||||
ref args,
|
||||
ref mut finished,
|
||||
ref host_func_index,
|
||||
} => {
|
||||
if *finished {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
*finished = true;
|
||||
Ok(externals.invoke_index(*host_func_index, args.clone().into())?)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,17 +288,21 @@ impl<'args> FuncInvocation<'args> {
|
|||
///
|
||||
/// [`resumable_value_type`]: #method.resumable_value_type
|
||||
/// [`is_resumable`]: #method.is_resumable
|
||||
pub fn resume_execution<'externals, E: Externals + 'externals>(&mut self, return_val: Option<RuntimeValue>, externals: &'externals mut E) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
pub fn resume_execution<'externals, E: Externals + 'externals>(
|
||||
&mut self,
|
||||
return_val: Option<RuntimeValue>,
|
||||
externals: &'externals mut E,
|
||||
) -> Result<Option<RuntimeValue>, ResumableError> {
|
||||
match self.kind {
|
||||
FuncInvocationKind::Internal(ref mut interpreter) => {
|
||||
if !interpreter.state().is_resumable() {
|
||||
return Err(ResumableError::AlreadyStarted);
|
||||
}
|
||||
Ok(interpreter.resume_execution(return_val, externals)?)
|
||||
},
|
||||
}
|
||||
FuncInvocationKind::Host { .. } => {
|
||||
return Err(ResumableError::NotResumable);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use alloc::rc::Rc;
|
||||
use core::cell::Cell;
|
||||
use parity_wasm::elements::ValueType as EValueType;
|
||||
use types::ValueType;
|
||||
use value::RuntimeValue;
|
||||
use Error;
|
||||
use types::ValueType;
|
||||
use parity_wasm::elements::{ValueType as EValueType};
|
||||
|
||||
/// Reference to a global variable (See [`GlobalInstance`] for details).
|
||||
///
|
||||
|
@ -57,7 +57,9 @@ impl GlobalInstance {
|
|||
/// type of `val` doesn't match global's type.
|
||||
pub fn set(&self, val: RuntimeValue) -> Result<(), Error> {
|
||||
if !self.mutable {
|
||||
return Err(Error::Global("Attempt to change an immutable variable".into()));
|
||||
return Err(Error::Global(
|
||||
"Attempt to change an immutable variable".into(),
|
||||
));
|
||||
}
|
||||
if self.value_type() != val.value_type() {
|
||||
return Err(Error::Global("Attempt to change variable type".into()));
|
||||
|
|
24
src/host.rs
24
src/host.rs
|
@ -1,6 +1,6 @@
|
|||
use core::any::TypeId;
|
||||
use value::{RuntimeValue, FromRuntimeValue};
|
||||
use {TrapKind, Trap};
|
||||
use value::{FromRuntimeValue, RuntimeValue};
|
||||
use {Trap, TrapKind};
|
||||
|
||||
/// Wrapper around slice of [`RuntimeValue`] for using it
|
||||
/// as an argument list conveniently.
|
||||
|
@ -27,8 +27,14 @@ impl<'a> RuntimeArgs<'a> {
|
|||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if cast is invalid or not enough arguments.
|
||||
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap> where T: FromRuntimeValue {
|
||||
Ok(self.nth_value_checked(idx)?.try_into().ok_or_else(|| TrapKind::UnexpectedSignature)?)
|
||||
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap>
|
||||
where
|
||||
T: FromRuntimeValue,
|
||||
{
|
||||
Ok(self
|
||||
.nth_value_checked(idx)?
|
||||
.try_into()
|
||||
.ok_or_else(|| TrapKind::UnexpectedSignature)?)
|
||||
}
|
||||
|
||||
/// Extract argument as a [`RuntimeValue`] by index `idx`.
|
||||
|
@ -50,7 +56,10 @@ impl<'a> RuntimeArgs<'a> {
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if cast is invalid or not enough arguments.
|
||||
pub fn nth<T>(&self, idx: usize) -> T where T: FromRuntimeValue {
|
||||
pub fn nth<T>(&self, idx: usize) -> T
|
||||
where
|
||||
T: FromRuntimeValue,
|
||||
{
|
||||
let value = self.nth_value_checked(idx).expect("Invalid argument index");
|
||||
value.try_into().expect("Unexpected argument type")
|
||||
}
|
||||
|
@ -225,8 +234,8 @@ impl Externals for NopExternals {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{HostError, RuntimeArgs};
|
||||
use value::RuntimeValue;
|
||||
use super::{RuntimeArgs, HostError};
|
||||
|
||||
#[test]
|
||||
fn i32_runtime_args() {
|
||||
|
@ -242,6 +251,5 @@ mod tests {
|
|||
}
|
||||
|
||||
// Tests that `HostError` trait is object safe.
|
||||
fn _host_error_is_object_safe(_: &HostError) {
|
||||
}
|
||||
fn _host_error_is_object_safe(_: &HostError) {}
|
||||
}
|
||||
|
|
112
src/imports.rs
112
src/imports.rs
|
@ -1,20 +1,19 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashMap;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
use func::FuncRef;
|
||||
use global::GlobalRef;
|
||||
use memory::MemoryRef;
|
||||
use func::FuncRef;
|
||||
use table::TableRef;
|
||||
use module::ModuleRef;
|
||||
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||
use table::TableRef;
|
||||
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
|
||||
use {Error, Signature};
|
||||
|
||||
|
||||
/// Resolver of a module's dependencies.
|
||||
///
|
||||
/// A module have dependencies in a form of a list of imports (i.e.
|
||||
|
@ -27,7 +26,6 @@ use {Error, Signature};
|
|||
///
|
||||
/// [`ImportsBuilder`]: struct.ImportsBuilder.html
|
||||
pub trait ImportResolver {
|
||||
|
||||
/// Resolve a function.
|
||||
///
|
||||
/// Returned function should match given `signature`, i.e. all parameter types and return value should have exact match.
|
||||
|
@ -120,7 +118,9 @@ impl<'a> Default for ImportsBuilder<'a> {
|
|||
impl<'a> ImportsBuilder<'a> {
|
||||
/// Create an empty `ImportsBuilder`.
|
||||
pub fn new() -> ImportsBuilder<'a> {
|
||||
ImportsBuilder { modules: HashMap::new() }
|
||||
ImportsBuilder {
|
||||
modules: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Register an resolver by a name.
|
||||
|
@ -152,9 +152,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
|
|||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
self.resolver(module_name).ok_or_else(||
|
||||
Error::Instantiation(format!("Module {} not found", module_name))
|
||||
)?.resolve_func(field_name, signature)
|
||||
self.resolver(module_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
|
||||
.resolve_func(field_name, signature)
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
|
@ -163,9 +163,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
|
|||
field_name: &str,
|
||||
global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
self.resolver(module_name).ok_or_else(||
|
||||
Error::Instantiation(format!("Module {} not found", module_name))
|
||||
)?.resolve_global(field_name, global_type)
|
||||
self.resolver(module_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
|
||||
.resolve_global(field_name, global_type)
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
|
@ -174,9 +174,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
|
|||
field_name: &str,
|
||||
memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
self.resolver(module_name).ok_or_else(||
|
||||
Error::Instantiation(format!("Module {} not found", module_name))
|
||||
)?.resolve_memory(field_name, memory_type)
|
||||
self.resolver(module_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
|
||||
.resolve_memory(field_name, memory_type)
|
||||
}
|
||||
|
||||
fn resolve_table(
|
||||
|
@ -185,9 +185,9 @@ impl<'a> ImportResolver for ImportsBuilder<'a> {
|
|||
field_name: &str,
|
||||
table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
self.resolver(module_name).ok_or_else(||
|
||||
Error::Instantiation(format!("Module {} not found", module_name))
|
||||
)?.resolve_table(field_name, table_type)
|
||||
self.resolver(module_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))?
|
||||
.resolve_table(field_name, table_type)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,14 +200,11 @@ pub trait ModuleImportResolver {
|
|||
/// See [`ImportResolver::resolve_func`] for details.
|
||||
///
|
||||
/// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
|
||||
/// Resolve a global variable.
|
||||
|
@ -220,9 +217,10 @@ pub trait ModuleImportResolver {
|
|||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
|
||||
/// Resolve a memory.
|
||||
|
@ -235,9 +233,10 @@ pub trait ModuleImportResolver {
|
|||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
|
||||
/// Resolve a table.
|
||||
|
@ -250,22 +249,18 @@ pub trait ModuleImportResolver {
|
|||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleImportResolver for ModuleRef {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
Ok(self.export_by_name(field_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} not found", field_name))
|
||||
})?
|
||||
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
|
||||
Ok(self
|
||||
.export_by_name(field_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||
.as_func()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
|
@ -278,10 +273,9 @@ impl ModuleImportResolver for ModuleRef {
|
|||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, Error> {
|
||||
Ok(self.export_by_name(field_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} not found", field_name))
|
||||
})?
|
||||
Ok(self
|
||||
.export_by_name(field_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||
.as_global()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
|
@ -294,10 +288,9 @@ impl ModuleImportResolver for ModuleRef {
|
|||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Ok(self.export_by_name(field_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} not found", field_name))
|
||||
})?
|
||||
Ok(self
|
||||
.export_by_name(field_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||
.as_memory()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
|
@ -310,14 +303,11 @@ impl ModuleImportResolver for ModuleRef {
|
|||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
Ok(self.export_by_name(field_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} not found", field_name))
|
||||
})?
|
||||
Ok(self
|
||||
.export_by_name(field_name)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))?
|
||||
.as_table()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Error::Instantiation(format!("Export {} is not a table", field_name))
|
||||
})?)
|
||||
.ok_or_else(|| Error::Instantiation(format!("Export {} is not a table", field_name)))?)
|
||||
}
|
||||
}
|
||||
|
|
23
src/isa.rs
23
src/isa.rs
|
@ -101,15 +101,10 @@ pub struct Target {
|
|||
pub enum Reloc {
|
||||
/// Patch the destination of the branch instruction (br, br_eqz, br_nez)
|
||||
/// at the specified pc.
|
||||
Br {
|
||||
pc: u32,
|
||||
},
|
||||
Br { pc: u32 },
|
||||
/// Patch the specified destination index inside of br_table instruction at
|
||||
/// the specified pc.
|
||||
BrTable {
|
||||
pc: u32,
|
||||
idx: usize,
|
||||
},
|
||||
BrTable { pc: u32, idx: usize },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -347,12 +342,12 @@ impl Instructions {
|
|||
Reloc::BrTable { pc, idx } => match self.vec[pc as usize] {
|
||||
Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc,
|
||||
_ => panic!("brtable relocation points to not brtable instruction"),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iterate_from(&self, position: u32) -> InstructionIter {
|
||||
InstructionIter{
|
||||
InstructionIter {
|
||||
instructions: &self.vec,
|
||||
position,
|
||||
}
|
||||
|
@ -376,9 +371,11 @@ impl<'a> Iterator for InstructionIter<'a> {
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
||||
self.instructions.get(self.position as usize).map(|instruction| {
|
||||
self.position += 1;
|
||||
instruction
|
||||
})
|
||||
self.instructions
|
||||
.get(self.position as usize)
|
||||
.map(|instruction| {
|
||||
self.position += 1;
|
||||
instruction
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
62
src/lib.rs
62
src/lib.rs
|
@ -95,9 +95,7 @@
|
|||
//! ```
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
//// alloc is required in no_std
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
|
@ -117,11 +115,11 @@ extern crate wabt;
|
|||
#[macro_use]
|
||||
extern crate assert_matches;
|
||||
|
||||
extern crate parity_wasm;
|
||||
extern crate byteorder;
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate hashmap_core;
|
||||
extern crate memory_units as memory_units_crate;
|
||||
extern crate parity_wasm;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
|
@ -292,7 +290,7 @@ impl Error {
|
|||
Error::Trap(ref trap) => match *trap.kind() {
|
||||
TrapKind::Host(ref host_err) => Some(&**host_err),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -347,13 +345,19 @@ impl error::Error for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl<U> From<U> for Error where U: host::HostError + Sized {
|
||||
impl<U> From<U> for Error
|
||||
where
|
||||
U: host::HostError + Sized,
|
||||
{
|
||||
fn from(e: U) -> Self {
|
||||
Error::Host(Box::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> From<U> for Trap where U: host::HostError + Sized {
|
||||
impl<U> From<U> for Trap
|
||||
where
|
||||
U: host::HostError + Sized,
|
||||
{
|
||||
fn from(e: U) -> Self {
|
||||
Trap::new(TrapKind::Host(Box::new(e)))
|
||||
}
|
||||
|
@ -377,38 +381,38 @@ impl From<validation::Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
mod validation;
|
||||
mod common;
|
||||
mod memory;
|
||||
mod module;
|
||||
mod runner;
|
||||
mod table;
|
||||
mod value;
|
||||
mod func;
|
||||
mod global;
|
||||
mod host;
|
||||
mod imports;
|
||||
mod global;
|
||||
mod func;
|
||||
mod types;
|
||||
mod isa;
|
||||
mod memory;
|
||||
mod module;
|
||||
pub mod nan_preserving_float;
|
||||
mod runner;
|
||||
mod table;
|
||||
mod types;
|
||||
mod validation;
|
||||
mod value;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
|
||||
pub use self::table::{TableInstance, TableRef};
|
||||
pub use self::value::{RuntimeValue, FromRuntimeValue};
|
||||
pub use self::host::{Externals, NopExternals, HostError, RuntimeArgs};
|
||||
pub use self::imports::{ModuleImportResolver, ImportResolver, ImportsBuilder};
|
||||
pub use self::module::{ModuleInstance, ModuleRef, ExternVal, NotStartedModuleRef};
|
||||
pub use self::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError};
|
||||
pub use self::global::{GlobalInstance, GlobalRef};
|
||||
pub use self::func::{FuncInstance, FuncRef, FuncInvocation, ResumableError};
|
||||
pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||
pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs};
|
||||
pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver};
|
||||
pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
|
||||
pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef};
|
||||
pub use self::table::{TableInstance, TableRef};
|
||||
pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType};
|
||||
pub use self::value::{FromRuntimeValue, RuntimeValue};
|
||||
|
||||
/// WebAssembly-specific sizes and units.
|
||||
pub mod memory_units {
|
||||
pub use memory_units_crate::wasm32::*;
|
||||
pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of};
|
||||
pub use memory_units_crate::{size_of, ByteSize, Bytes, RoundUpTo};
|
||||
}
|
||||
|
||||
/// Deserialized module prepared for instantiation.
|
||||
|
@ -452,15 +456,9 @@ impl Module {
|
|||
/// ```
|
||||
pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> {
|
||||
use validation::{validate_module, ValidatedModule};
|
||||
let ValidatedModule {
|
||||
code_map,
|
||||
module,
|
||||
} = validate_module(module)?;
|
||||
let ValidatedModule { code_map, module } = validate_module(module)?;
|
||||
|
||||
Ok(Module {
|
||||
code_map,
|
||||
module,
|
||||
})
|
||||
Ok(Module { code_map, module })
|
||||
}
|
||||
|
||||
/// Fail if the module contains any floating-point operations
|
||||
|
|
222
src/memory.rs
222
src/memory.rs
|
@ -1,15 +1,15 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::u32;
|
||||
use core::ops::Range;
|
||||
use core::cell::{Cell, RefCell};
|
||||
use core::cmp;
|
||||
use core::fmt;
|
||||
use core::cell::{Cell, RefCell};
|
||||
use core::ops::Range;
|
||||
use core::u32;
|
||||
use memory_units::{Bytes, Pages, RoundUpTo};
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
use memory_units::{RoundUpTo, Pages, Bytes};
|
||||
use value::LittleEndianConvert;
|
||||
use Error;
|
||||
|
||||
/// Size of a page of [linear memory][`MemoryInstance`] - 64KiB.
|
||||
///
|
||||
|
@ -78,7 +78,7 @@ struct CheckedRegion {
|
|||
|
||||
impl CheckedRegion {
|
||||
fn range(&self) -> Range<usize> {
|
||||
self.offset..self.offset+self.size
|
||||
self.offset..self.offset + self.size
|
||||
}
|
||||
|
||||
fn intersects(&self, other: &Self) -> bool {
|
||||
|
@ -174,7 +174,8 @@ impl MemoryInstance {
|
|||
/// Get value from memory at given offset.
|
||||
pub fn get_value<T: LittleEndianConvert>(&self, offset: u32) -> Result<T, Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
|
||||
let region =
|
||||
self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?;
|
||||
Ok(T::from_little_endian(&buffer[region.range()]).expect("Slice size is checked"))
|
||||
}
|
||||
|
||||
|
@ -208,7 +209,9 @@ impl MemoryInstance {
|
|||
/// Copy data in the memory at given offset.
|
||||
pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let range = self.checked_region(&mut buffer, offset as usize, value.len())?.range();
|
||||
let range = self
|
||||
.checked_region(&mut buffer, offset as usize, value.len())?
|
||||
.range();
|
||||
|
||||
buffer[range].copy_from_slice(value);
|
||||
|
||||
|
@ -218,7 +221,9 @@ impl MemoryInstance {
|
|||
/// Copy value in the memory at given offset.
|
||||
pub fn set_value<T: LittleEndianConvert>(&self, offset: u32, value: T) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let range = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?.range();
|
||||
let range = self
|
||||
.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::<T>())?
|
||||
.range();
|
||||
value.into_little_endian(&mut buffer[range]);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -255,18 +260,33 @@ impl MemoryInstance {
|
|||
Ok(size_before_grow)
|
||||
}
|
||||
|
||||
fn checked_region<B>(&self, buffer: &mut B, offset: usize, size: usize) -> Result<CheckedRegion, Error>
|
||||
where B: ::core::ops::DerefMut<Target=Vec<u8>>
|
||||
fn checked_region<B>(
|
||||
&self,
|
||||
buffer: &mut B,
|
||||
offset: usize,
|
||||
size: usize,
|
||||
) -> Result<CheckedRegion, Error>
|
||||
where
|
||||
B: ::core::ops::DerefMut<Target = Vec<u8>>,
|
||||
{
|
||||
let end = offset.checked_add(size)
|
||||
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;
|
||||
let end = offset.checked_add(size).ok_or_else(|| {
|
||||
Error::Memory(format!(
|
||||
"trying to access memory block of size {} from offset {}",
|
||||
size, offset
|
||||
))
|
||||
})?;
|
||||
|
||||
if end <= self.current_size.get() && buffer.len() < end {
|
||||
buffer.resize(end, 0);
|
||||
}
|
||||
|
||||
if end > buffer.len() {
|
||||
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len())));
|
||||
return Err(Error::Memory(format!(
|
||||
"trying to access region [{}..{}] in memory [0..{}]",
|
||||
offset,
|
||||
end,
|
||||
buffer.len()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(CheckedRegion {
|
||||
|
@ -275,15 +295,30 @@ impl MemoryInstance {
|
|||
})
|
||||
}
|
||||
|
||||
fn checked_region_pair<B>(&self, buffer: &mut B, offset1: usize, size1: usize, offset2: usize, size2: usize)
|
||||
-> Result<(CheckedRegion, CheckedRegion), Error>
|
||||
where B: ::core::ops::DerefMut<Target=Vec<u8>>
|
||||
fn checked_region_pair<B>(
|
||||
&self,
|
||||
buffer: &mut B,
|
||||
offset1: usize,
|
||||
size1: usize,
|
||||
offset2: usize,
|
||||
size2: usize,
|
||||
) -> Result<(CheckedRegion, CheckedRegion), Error>
|
||||
where
|
||||
B: ::core::ops::DerefMut<Target = Vec<u8>>,
|
||||
{
|
||||
let end1 = offset1.checked_add(size1)
|
||||
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size1, offset1)))?;
|
||||
let end1 = offset1.checked_add(size1).ok_or_else(|| {
|
||||
Error::Memory(format!(
|
||||
"trying to access memory block of size {} from offset {}",
|
||||
size1, offset1
|
||||
))
|
||||
})?;
|
||||
|
||||
let end2 = offset2.checked_add(size2)
|
||||
.ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size2, offset2)))?;
|
||||
let end2 = offset2.checked_add(size2).ok_or_else(|| {
|
||||
Error::Memory(format!(
|
||||
"trying to access memory block of size {} from offset {}",
|
||||
size2, offset2
|
||||
))
|
||||
})?;
|
||||
|
||||
let max = cmp::max(end1, end2);
|
||||
if max <= self.current_size.get() && buffer.len() < max {
|
||||
|
@ -291,16 +326,32 @@ impl MemoryInstance {
|
|||
}
|
||||
|
||||
if end1 > buffer.len() {
|
||||
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset1, end1, buffer.len())));
|
||||
return Err(Error::Memory(format!(
|
||||
"trying to access region [{}..{}] in memory [0..{}]",
|
||||
offset1,
|
||||
end1,
|
||||
buffer.len()
|
||||
)));
|
||||
}
|
||||
|
||||
if end2 > buffer.len() {
|
||||
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset2, end2, buffer.len())));
|
||||
return Err(Error::Memory(format!(
|
||||
"trying to access region [{}..{}] in memory [0..{}]",
|
||||
offset2,
|
||||
end2,
|
||||
buffer.len()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok((
|
||||
CheckedRegion { offset: offset1, size: size1 },
|
||||
CheckedRegion { offset: offset2, size: size2 },
|
||||
CheckedRegion {
|
||||
offset: offset1,
|
||||
size: size1,
|
||||
},
|
||||
CheckedRegion {
|
||||
offset: offset2,
|
||||
size: size2,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -314,13 +365,16 @@ impl MemoryInstance {
|
|||
pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
let (read_region, write_region) =
|
||||
self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
|
||||
unsafe { ::core::ptr::copy(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
)}
|
||||
unsafe {
|
||||
::core::ptr::copy(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -336,20 +390,30 @@ impl MemoryInstance {
|
|||
///
|
||||
/// - either of specified regions is out of bounds,
|
||||
/// - these regions overlaps.
|
||||
pub fn copy_nonoverlapping(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
|
||||
pub fn copy_nonoverlapping(
|
||||
&self,
|
||||
src_offset: usize,
|
||||
dst_offset: usize,
|
||||
len: usize,
|
||||
) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
let (read_region, write_region) =
|
||||
self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?;
|
||||
|
||||
if read_region.intersects(&write_region) {
|
||||
return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions")))
|
||||
return Err(Error::Memory(format!(
|
||||
"non-overlapping copy is used for overlapping regions"
|
||||
)));
|
||||
}
|
||||
|
||||
unsafe { ::core::ptr::copy_nonoverlapping(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
)}
|
||||
unsafe {
|
||||
::core::ptr::copy_nonoverlapping(
|
||||
buffer[read_region.range()].as_ptr(),
|
||||
buffer[write_region.range()].as_mut_ptr(),
|
||||
len,
|
||||
)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -357,7 +421,13 @@ impl MemoryInstance {
|
|||
/// Copy memory between two (possibly distinct) memory instances.
|
||||
///
|
||||
/// If the same memory instance passed as `src` and `dst` then usual `copy` will be used.
|
||||
pub fn transfer(src: &MemoryRef, src_offset: usize, dst: &MemoryRef, dst_offset: usize, len: usize) -> Result<(), Error> {
|
||||
pub fn transfer(
|
||||
src: &MemoryRef,
|
||||
src_offset: usize,
|
||||
dst: &MemoryRef,
|
||||
dst_offset: usize,
|
||||
len: usize,
|
||||
) -> Result<(), Error> {
|
||||
if Rc::ptr_eq(&src.0, &dst.0) {
|
||||
// `transfer` is invoked with with same source and destination. Let's assume that regions may
|
||||
// overlap and use `copy`.
|
||||
|
@ -369,8 +439,12 @@ impl MemoryInstance {
|
|||
let mut src_buffer = src.buffer.borrow_mut();
|
||||
let mut dst_buffer = dst.buffer.borrow_mut();
|
||||
|
||||
let src_range = src.checked_region(&mut src_buffer, src_offset, len)?.range();
|
||||
let dst_range = dst.checked_region(&mut dst_buffer, dst_offset, len)?.range();
|
||||
let src_range = src
|
||||
.checked_region(&mut src_buffer, src_offset, len)?
|
||||
.range();
|
||||
let dst_range = dst
|
||||
.checked_region(&mut dst_buffer, dst_offset, len)?
|
||||
.range();
|
||||
|
||||
dst_buffer[dst_range].copy_from_slice(&src_buffer[src_range]);
|
||||
|
||||
|
@ -388,7 +462,9 @@ impl MemoryInstance {
|
|||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
let range = self.checked_region(&mut buffer, offset, len)?.range();
|
||||
for val in &mut buffer[range] { *val = new_val }
|
||||
for val in &mut buffer[range] {
|
||||
*val = new_val
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -434,19 +510,24 @@ impl MemoryInstance {
|
|||
|
||||
pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), String> {
|
||||
if initial > LINEAR_MEMORY_MAX_PAGES {
|
||||
return Err(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
|
||||
return Err(format!(
|
||||
"initial memory size must be at most {} pages",
|
||||
LINEAR_MEMORY_MAX_PAGES.0
|
||||
));
|
||||
}
|
||||
if let Some(maximum) = maximum {
|
||||
if initial > maximum {
|
||||
return Err(format!(
|
||||
"maximum limit {} is less than minimum {}",
|
||||
maximum.0,
|
||||
initial.0,
|
||||
maximum.0, initial.0,
|
||||
));
|
||||
}
|
||||
|
||||
if maximum > LINEAR_MEMORY_MAX_PAGES {
|
||||
return Err(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
|
||||
return Err(format!(
|
||||
"maximum memory size must be at most {} pages",
|
||||
LINEAR_MEMORY_MAX_PAGES.0
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -455,10 +536,10 @@ pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), Str
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{MemoryRef, MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
|
||||
use super::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
|
||||
use memory_units::Pages;
|
||||
use std::rc::Rc;
|
||||
use Error;
|
||||
use memory_units::Pages;
|
||||
|
||||
#[test]
|
||||
fn alloc() {
|
||||
|
@ -493,11 +574,7 @@ mod tests {
|
|||
if result.is_ok() != expected_ok {
|
||||
panic!(
|
||||
"unexpected error at {}, initial={:?}, max={:?}, expected={}, result={:?}",
|
||||
index,
|
||||
initial,
|
||||
maybe_max,
|
||||
expected_ok,
|
||||
result,
|
||||
index, initial, maybe_max, expected_ok, result,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -511,7 +588,8 @@ mod tests {
|
|||
|
||||
fn create_memory(initial_content: &[u8]) -> MemoryInstance {
|
||||
let mem = MemoryInstance::new(Pages(1), Some(Pages(1)));
|
||||
mem.set(0, initial_content).expect("Successful initialize the memory");
|
||||
mem.set(0, initial_content)
|
||||
.expect("Successful initialize the memory");
|
||||
mem
|
||||
}
|
||||
|
||||
|
@ -534,7 +612,8 @@ mod tests {
|
|||
#[test]
|
||||
fn copy_nonoverlapping() {
|
||||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
mem.copy_nonoverlapping(0, 10, 10).expect("Successfully copy the elements");
|
||||
mem.copy_nonoverlapping(0, 10, 10)
|
||||
.expect("Successfully copy the elements");
|
||||
let result = mem.get(10, 10).expect("Successfully retrieve the result");
|
||||
assert_eq!(result, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
}
|
||||
|
@ -544,7 +623,7 @@ mod tests {
|
|||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let result = mem.copy_nonoverlapping(0, 4, 6);
|
||||
match result {
|
||||
Err(Error::Memory(_)) => {},
|
||||
Err(Error::Memory(_)) => {}
|
||||
_ => panic!("Expected Error::Memory(_) result, but got {:?}", result),
|
||||
}
|
||||
}
|
||||
|
@ -554,7 +633,7 @@ mod tests {
|
|||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let result = mem.copy_nonoverlapping(4, 0, 6);
|
||||
match result {
|
||||
Err(Error::Memory(_)) => {},
|
||||
Err(Error::Memory(_)) => {}
|
||||
_ => panic!("Expected Error::Memory(_), but got {:?}", result),
|
||||
}
|
||||
}
|
||||
|
@ -562,12 +641,17 @@ mod tests {
|
|||
#[test]
|
||||
fn transfer_works() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
])));
|
||||
|
||||
MemoryInstance::transfer(&src, 4, &dst, 0, 3).unwrap();
|
||||
|
||||
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
assert_eq!(dst.get(0, 10).unwrap(), &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]);
|
||||
assert_eq!(
|
||||
dst.get(0, 10).unwrap(),
|
||||
&[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -591,19 +675,25 @@ mod tests {
|
|||
#[test]
|
||||
fn transfer_oob_errors() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
])));
|
||||
|
||||
assert!(MemoryInstance::transfer(&src, 65535, &dst, 0, 3).is_err());
|
||||
|
||||
// Check that memories content left untouched
|
||||
assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
assert_eq!(dst.get(0, 10).unwrap(), &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
|
||||
assert_eq!(
|
||||
dst.get(0, 10).unwrap(),
|
||||
&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear() {
|
||||
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
mem.clear(0, 0x4A, 10).expect("To successfully clear the memory");
|
||||
mem.clear(0, 0x4A, 10)
|
||||
.expect("To successfully clear the memory");
|
||||
let result = mem.get(0, 10).expect("To successfully retrieve the result");
|
||||
assert_eq!(result, &[0x4A; 10]);
|
||||
}
|
||||
|
@ -611,10 +701,12 @@ mod tests {
|
|||
#[test]
|
||||
fn get_into() {
|
||||
let mem = MemoryInstance::new(Pages(1), None);
|
||||
mem.set(6, &[13, 17, 129]).expect("memory set should not fail");
|
||||
mem.set(6, &[13, 17, 129])
|
||||
.expect("memory set should not fail");
|
||||
|
||||
let mut data = [0u8; 2];
|
||||
mem.get_into(7, &mut data[..]).expect("get_into should not fail");
|
||||
mem.get_into(7, &mut data[..])
|
||||
.expect("get_into should not fail");
|
||||
|
||||
assert_eq!(data, [17, 129]);
|
||||
}
|
||||
|
|
209
src/module.rs
209
src/module.rs
|
@ -1,26 +1,26 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use Trap;
|
||||
use core::cell::RefCell;
|
||||
use core::fmt;
|
||||
use Trap;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashMap;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type};
|
||||
use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance};
|
||||
use imports::ImportResolver;
|
||||
use global::{GlobalInstance, GlobalRef};
|
||||
use func::{FuncRef, FuncBody, FuncInstance};
|
||||
use table::TableRef;
|
||||
use memory::MemoryRef;
|
||||
use host::Externals;
|
||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||
use func::{FuncBody, FuncInstance, FuncRef};
|
||||
use global::{GlobalInstance, GlobalRef};
|
||||
use host::Externals;
|
||||
use imports::ImportResolver;
|
||||
use memory::MemoryRef;
|
||||
use memory_units::Pages;
|
||||
use parity_wasm::elements::{External, InitExpr, Instruction, Internal, ResizableLimits, Type};
|
||||
use table::TableRef;
|
||||
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
|
||||
use {Error, MemoryInstance, Module, RuntimeValue, Signature, TableInstance};
|
||||
|
||||
/// Reference to a [`ModuleInstance`].
|
||||
///
|
||||
|
@ -254,9 +254,9 @@ impl ModuleInstance {
|
|||
|
||||
match (import.external(), extern_val) {
|
||||
(&External::Function(fn_type_idx), &ExternVal::Func(ref func)) => {
|
||||
let expected_fn_type = instance.signature_by_index(fn_type_idx).expect(
|
||||
"Due to validation function type should exists",
|
||||
);
|
||||
let expected_fn_type = instance
|
||||
.signature_by_index(fn_type_idx)
|
||||
.expect("Due to validation function type should exists");
|
||||
let actual_fn_type = func.signature();
|
||||
if &*expected_fn_type != actual_fn_type {
|
||||
return Err(Error::Instantiation(format!(
|
||||
|
@ -289,8 +289,7 @@ impl ModuleInstance {
|
|||
(expected_import, actual_extern_val) => {
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Expected {:?} type, but provided {:?} extern_val",
|
||||
expected_import,
|
||||
actual_extern_val
|
||||
expected_import, actual_extern_val
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -299,9 +298,10 @@ impl ModuleInstance {
|
|||
|
||||
let code = loaded_module.code();
|
||||
{
|
||||
let funcs = module.function_section().map(|fs| fs.entries()).unwrap_or(
|
||||
&[],
|
||||
);
|
||||
let funcs = module
|
||||
.function_section()
|
||||
.map(|fs| fs.entries())
|
||||
.unwrap_or(&[]);
|
||||
let bodies = module.code_section().map(|cs| cs.bodies()).unwrap_or(&[]);
|
||||
debug_assert!(
|
||||
funcs.len() == bodies.len(),
|
||||
|
@ -311,9 +311,9 @@ impl ModuleInstance {
|
|||
for (index, (ty, body)) in
|
||||
Iterator::zip(funcs.into_iter(), bodies.into_iter()).enumerate()
|
||||
{
|
||||
let signature = instance.signature_by_index(ty.type_ref()).expect(
|
||||
"Due to validation type should exists",
|
||||
);
|
||||
let signature = instance
|
||||
.signature_by_index(ty.type_ref())
|
||||
.expect("Due to validation type should exists");
|
||||
let code = code.get(index).expect(
|
||||
"At func validation time labels are collected; Collected labels are added by index; qed",
|
||||
).clone();
|
||||
|
@ -328,16 +328,15 @@ impl ModuleInstance {
|
|||
}
|
||||
|
||||
for table_type in module.table_section().map(|ts| ts.entries()).unwrap_or(&[]) {
|
||||
let table = TableInstance::alloc(
|
||||
table_type.limits().initial(),
|
||||
table_type.limits().maximum(),
|
||||
)?;
|
||||
let table =
|
||||
TableInstance::alloc(table_type.limits().initial(), table_type.limits().maximum())?;
|
||||
instance.push_table(table);
|
||||
}
|
||||
|
||||
for memory_type in module.memory_section().map(|ms| ms.entries()).unwrap_or(
|
||||
&[],
|
||||
)
|
||||
for memory_type in module
|
||||
.memory_section()
|
||||
.map(|ms| ms.entries())
|
||||
.unwrap_or(&[])
|
||||
{
|
||||
let initial: Pages = Pages(memory_type.limits().initial() as usize);
|
||||
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
|
||||
|
@ -347,46 +346,45 @@ impl ModuleInstance {
|
|||
instance.push_memory(memory);
|
||||
}
|
||||
|
||||
for global_entry in module.global_section().map(|gs| gs.entries()).unwrap_or(
|
||||
&[],
|
||||
)
|
||||
for global_entry in module
|
||||
.global_section()
|
||||
.map(|gs| gs.entries())
|
||||
.unwrap_or(&[])
|
||||
{
|
||||
let init_val = eval_init_expr(global_entry.init_expr(), &*instance);
|
||||
let global = GlobalInstance::alloc(
|
||||
init_val,
|
||||
global_entry.global_type().is_mutable(),
|
||||
);
|
||||
let global = GlobalInstance::alloc(init_val, global_entry.global_type().is_mutable());
|
||||
instance.push_global(global);
|
||||
}
|
||||
|
||||
for export in module.export_section().map(|es| es.entries()).unwrap_or(
|
||||
&[],
|
||||
)
|
||||
for export in module
|
||||
.export_section()
|
||||
.map(|es| es.entries())
|
||||
.unwrap_or(&[])
|
||||
{
|
||||
let field = export.field();
|
||||
let extern_val: ExternVal = match *export.internal() {
|
||||
Internal::Function(idx) => {
|
||||
let func = instance.func_by_index(idx).expect(
|
||||
"Due to validation func should exists",
|
||||
);
|
||||
let func = instance
|
||||
.func_by_index(idx)
|
||||
.expect("Due to validation func should exists");
|
||||
ExternVal::Func(func)
|
||||
}
|
||||
Internal::Global(idx) => {
|
||||
let global = instance.global_by_index(idx).expect(
|
||||
"Due to validation global should exists",
|
||||
);
|
||||
let global = instance
|
||||
.global_by_index(idx)
|
||||
.expect("Due to validation global should exists");
|
||||
ExternVal::Global(global)
|
||||
}
|
||||
Internal::Memory(idx) => {
|
||||
let memory = instance.memory_by_index(idx).expect(
|
||||
"Due to validation memory should exists",
|
||||
);
|
||||
let memory = instance
|
||||
.memory_by_index(idx)
|
||||
.expect("Due to validation memory should exists");
|
||||
ExternVal::Memory(memory)
|
||||
}
|
||||
Internal::Table(idx) => {
|
||||
let table = instance.table_by_index(idx).expect(
|
||||
"Due to validation table should exists",
|
||||
);
|
||||
let table = instance
|
||||
.table_by_index(idx)
|
||||
.expect("Due to validation table should exists");
|
||||
ExternVal::Table(table)
|
||||
}
|
||||
};
|
||||
|
@ -410,31 +408,34 @@ impl ModuleInstance {
|
|||
|
||||
let module_ref = ModuleInstance::alloc_module(loaded_module, extern_vals)?;
|
||||
|
||||
for element_segment in module.elements_section().map(|es| es.entries()).unwrap_or(
|
||||
&[],
|
||||
)
|
||||
for element_segment in module
|
||||
.elements_section()
|
||||
.map(|es| es.entries())
|
||||
.unwrap_or(&[])
|
||||
{
|
||||
let offset_val = match eval_init_expr(element_segment.offset(), &module_ref) {
|
||||
RuntimeValue::I32(v) => v as u32,
|
||||
_ => panic!("Due to validation elem segment offset should evaluate to i32"),
|
||||
};
|
||||
|
||||
let table_inst = module_ref.table_by_index(DEFAULT_TABLE_INDEX).expect(
|
||||
"Due to validation default table should exists",
|
||||
);
|
||||
let table_inst = module_ref
|
||||
.table_by_index(DEFAULT_TABLE_INDEX)
|
||||
.expect("Due to validation default table should exists");
|
||||
|
||||
// This check is not only for bailing out early, but also to check the case when
|
||||
// segment consist of 0 members.
|
||||
if offset_val as u64 + element_segment.members().len() as u64 > table_inst.current_size() as u64 {
|
||||
return Err(
|
||||
Error::Instantiation("elements segment does not fit".to_string())
|
||||
);
|
||||
if offset_val as u64 + element_segment.members().len() as u64
|
||||
> table_inst.current_size() as u64
|
||||
{
|
||||
return Err(Error::Instantiation(
|
||||
"elements segment does not fit".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
for (j, func_idx) in element_segment.members().into_iter().enumerate() {
|
||||
let func = module_ref.func_by_index(*func_idx).expect(
|
||||
"Due to validation funcs from element segments should exists",
|
||||
);
|
||||
let func = module_ref
|
||||
.func_by_index(*func_idx)
|
||||
.expect("Due to validation funcs from element segments should exists");
|
||||
|
||||
table_inst.set(offset_val + j as u32, Some(func))?;
|
||||
}
|
||||
|
@ -446,9 +447,9 @@ impl ModuleInstance {
|
|||
_ => panic!("Due to validation data segment offset should evaluate to i32"),
|
||||
};
|
||||
|
||||
let memory_inst = module_ref.memory_by_index(DEFAULT_MEMORY_INDEX).expect(
|
||||
"Due to validation default memory should exists",
|
||||
);
|
||||
let memory_inst = module_ref
|
||||
.memory_by_index(DEFAULT_MEMORY_INDEX)
|
||||
.expect("Due to validation default memory should exists");
|
||||
memory_inst.set(offset_val, data_segment.value())?;
|
||||
}
|
||||
|
||||
|
@ -540,17 +541,20 @@ impl ModuleInstance {
|
|||
}
|
||||
External::Table(ref table_type) => {
|
||||
let table_descriptor = TableDescriptor::from_elements(table_type);
|
||||
let table = imports.resolve_table(module_name, field_name, &table_descriptor)?;
|
||||
let table =
|
||||
imports.resolve_table(module_name, field_name, &table_descriptor)?;
|
||||
ExternVal::Table(table)
|
||||
}
|
||||
External::Memory(ref memory_type) => {
|
||||
let memory_descriptor = MemoryDescriptor::from_elements(memory_type);
|
||||
let memory = imports.resolve_memory(module_name, field_name, &memory_descriptor)?;
|
||||
let memory =
|
||||
imports.resolve_memory(module_name, field_name, &memory_descriptor)?;
|
||||
ExternVal::Memory(memory)
|
||||
}
|
||||
External::Global(ref global_type) => {
|
||||
let global_descriptor = GlobalDescriptor::from_elements(global_type);
|
||||
let global = imports.resolve_global(module_name, field_name, &global_descriptor)?;
|
||||
let global =
|
||||
imports.resolve_global(module_name, field_name, &global_descriptor)?;
|
||||
ExternVal::Global(global)
|
||||
}
|
||||
};
|
||||
|
@ -614,23 +618,21 @@ impl ModuleInstance {
|
|||
args: &[RuntimeValue],
|
||||
externals: &mut E,
|
||||
) -> Result<Option<RuntimeValue>, Error> {
|
||||
let extern_val = self.export_by_name(func_name).ok_or_else(|| {
|
||||
Error::Function(format!("Module doesn't have export {}", func_name))
|
||||
})?;
|
||||
let extern_val = self
|
||||
.export_by_name(func_name)
|
||||
.ok_or_else(|| Error::Function(format!("Module doesn't have export {}", func_name)))?;
|
||||
|
||||
let func_instance = match extern_val {
|
||||
ExternVal::Func(func_instance) => func_instance,
|
||||
unexpected => {
|
||||
return Err(Error::Function(format!(
|
||||
"Export {} is not a function, but {:?}",
|
||||
func_name,
|
||||
unexpected
|
||||
func_name, unexpected
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
FuncInstance::invoke(&func_instance, args, externals)
|
||||
.map_err(|t| Error::Trap(t))
|
||||
FuncInstance::invoke(&func_instance, args, externals).map_err(|t| Error::Trap(t))
|
||||
}
|
||||
|
||||
/// Find export by a name.
|
||||
|
@ -685,9 +687,10 @@ impl<'a> NotStartedModuleRef<'a> {
|
|||
/// Returns `Err` if start function traps.
|
||||
pub fn run_start<E: Externals>(self, state: &mut E) -> Result<ModuleRef, Trap> {
|
||||
if let Some(start_fn_idx) = self.loaded_module.module().start_section() {
|
||||
let start_func = self.instance.func_by_index(start_fn_idx).expect(
|
||||
"Due to validation start function should exists",
|
||||
);
|
||||
let start_func = self
|
||||
.instance
|
||||
.func_by_index(start_fn_idx)
|
||||
.expect("Due to validation start function should exists");
|
||||
FuncInstance::invoke(&start_func, &[], state)?;
|
||||
}
|
||||
Ok(self.instance)
|
||||
|
@ -718,9 +721,9 @@ fn eval_init_expr(init_expr: &InitExpr, module: &ModuleInstance) -> RuntimeValue
|
|||
Instruction::F32Const(v) => RuntimeValue::decode_f32(v),
|
||||
Instruction::F64Const(v) => RuntimeValue::decode_f64(v),
|
||||
Instruction::GetGlobal(idx) => {
|
||||
let global = module.global_by_index(idx).expect(
|
||||
"Due to validation global should exists in module",
|
||||
);
|
||||
let global = module
|
||||
.global_by_index(idx)
|
||||
.expect("Due to validation global should exists in module");
|
||||
global.get()
|
||||
}
|
||||
_ => panic!("Due to validation init should be a const expr"),
|
||||
|
@ -765,14 +768,13 @@ pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use imports::ImportsBuilder;
|
||||
use super::{ExternVal, ModuleInstance};
|
||||
use func::FuncInstance;
|
||||
use types::{Signature, ValueType};
|
||||
use super::{ModuleInstance, ExternVal};
|
||||
use imports::ImportsBuilder;
|
||||
use tests::parse_wat;
|
||||
use types::{Signature, ValueType};
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
|
@ -782,12 +784,11 @@ mod tests {
|
|||
(module
|
||||
(func $f)
|
||||
(start $f))
|
||||
"#
|
||||
"#,
|
||||
);
|
||||
ModuleInstance::new(
|
||||
&module_with_start,
|
||||
&ImportsBuilder::default()
|
||||
).unwrap().assert_no_start();
|
||||
ModuleInstance::new(&module_with_start, &ImportsBuilder::default())
|
||||
.unwrap()
|
||||
.assert_no_start();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -803,9 +804,11 @@ mod tests {
|
|||
assert!(
|
||||
ModuleInstance::with_externvals(
|
||||
&module_with_single_import,
|
||||
[
|
||||
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0),)
|
||||
].iter(),
|
||||
[ExternVal::Func(FuncInstance::alloc_host(
|
||||
Signature::new(&[][..], None),
|
||||
0
|
||||
),)]
|
||||
.iter(),
|
||||
).is_ok()
|
||||
);
|
||||
|
||||
|
@ -816,7 +819,8 @@ mod tests {
|
|||
[
|
||||
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0)),
|
||||
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 1)),
|
||||
].iter(),
|
||||
]
|
||||
.iter(),
|
||||
).is_err()
|
||||
);
|
||||
|
||||
|
@ -827,12 +831,11 @@ mod tests {
|
|||
assert!(
|
||||
ModuleInstance::with_externvals(
|
||||
&module_with_single_import,
|
||||
[
|
||||
ExternVal::Func(FuncInstance::alloc_host(
|
||||
Signature::new(&[][..], Some(ValueType::I32)),
|
||||
0
|
||||
),)
|
||||
].iter(),
|
||||
[ExternVal::Func(FuncInstance::alloc_host(
|
||||
Signature::new(&[][..], Some(ValueType::I32)),
|
||||
0
|
||||
),)]
|
||||
.iter(),
|
||||
).is_err()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,210 +3,213 @@
|
|||
#[cfg(not(feature = "std"))]
|
||||
use libm::{F32Ext, F64Ext};
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub, Rem};
|
||||
use core::cmp::{Ordering, PartialEq, PartialOrd};
|
||||
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
|
||||
|
||||
macro_rules! impl_binop {
|
||||
($for:ident, $is:ident, $op:ident, $func_name:ident) => {
|
||||
impl<T: Into<$for>> $op<T> for $for {
|
||||
type Output = Self;
|
||||
($for:ident, $is:ident, $op:ident, $func_name:ident) => {
|
||||
impl<T: Into<$for>> $op<T> for $for {
|
||||
type Output = Self;
|
||||
|
||||
fn $func_name(self, other: T) -> Self {
|
||||
$for(
|
||||
$op::$func_name(
|
||||
$is::from_bits(self.0),
|
||||
$is::from_bits(other.into().0)
|
||||
).to_bits()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn $func_name(self, other: T) -> Self {
|
||||
$for(
|
||||
$op::$func_name($is::from_bits(self.0), $is::from_bits(other.into().0))
|
||||
.to_bits(),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! float {
|
||||
($for:ident, $rep:ident, $is:ident) => {
|
||||
float!($for, $rep, $is, 1 << (::core::mem::size_of::<$is>() * 8 - 1));
|
||||
};
|
||||
($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => {
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct $for($rep);
|
||||
($for:ident, $rep:ident, $is:ident) => {
|
||||
float!(
|
||||
$for,
|
||||
$rep,
|
||||
$is,
|
||||
1 << (::core::mem::size_of::<$is>() * 8 - 1)
|
||||
);
|
||||
};
|
||||
($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => {
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct $for($rep);
|
||||
|
||||
impl_binop!($for, $is, Add, add);
|
||||
impl_binop!($for, $is, Sub, sub);
|
||||
impl_binop!($for, $is, Mul, mul);
|
||||
impl_binop!($for, $is, Div, div);
|
||||
impl_binop!($for, $is, Rem, rem);
|
||||
impl_binop!($for, $is, Add, add);
|
||||
impl_binop!($for, $is, Sub, sub);
|
||||
impl_binop!($for, $is, Mul, mul);
|
||||
impl_binop!($for, $is, Div, div);
|
||||
impl_binop!($for, $is, Rem, rem);
|
||||
|
||||
impl $for {
|
||||
pub fn from_bits(other: $rep) -> Self {
|
||||
$for(other)
|
||||
}
|
||||
impl $for {
|
||||
pub fn from_bits(other: $rep) -> Self {
|
||||
$for(other)
|
||||
}
|
||||
|
||||
pub fn to_bits(self) -> $rep {
|
||||
self.0
|
||||
}
|
||||
pub fn to_bits(self) -> $rep {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn from_float(fl: $is) -> Self {
|
||||
fl.into()
|
||||
}
|
||||
pub fn from_float(fl: $is) -> Self {
|
||||
fl.into()
|
||||
}
|
||||
|
||||
pub fn to_float(self) -> $is {
|
||||
self.into()
|
||||
}
|
||||
pub fn to_float(self) -> $is {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn is_nan(self) -> bool {
|
||||
self.to_float().is_nan()
|
||||
}
|
||||
pub fn is_nan(self) -> bool {
|
||||
self.to_float().is_nan()
|
||||
}
|
||||
|
||||
pub fn abs(self) -> Self {
|
||||
$for(self.0 & !$sign_bit)
|
||||
}
|
||||
pub fn abs(self) -> Self {
|
||||
$for(self.0 & !$sign_bit)
|
||||
}
|
||||
|
||||
pub fn fract(self) -> Self {
|
||||
self.to_float().fract().into()
|
||||
}
|
||||
pub fn fract(self) -> Self {
|
||||
self.to_float().fract().into()
|
||||
}
|
||||
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
Self::from(self.to_float().min(other.to_float()))
|
||||
}
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
Self::from(self.to_float().min(other.to_float()))
|
||||
}
|
||||
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
Self::from(self.to_float().max(other.to_float()))
|
||||
}
|
||||
}
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
Self::from(self.to_float().max(other.to_float()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$is> for $for {
|
||||
fn from(other: $is) -> $for {
|
||||
$for(other.to_bits())
|
||||
}
|
||||
}
|
||||
impl From<$is> for $for {
|
||||
fn from(other: $is) -> $for {
|
||||
$for(other.to_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$for> for $is {
|
||||
fn from(other: $for) -> $is {
|
||||
<$is>::from_bits(other.0)
|
||||
}
|
||||
}
|
||||
impl From<$for> for $is {
|
||||
fn from(other: $for) -> $is {
|
||||
<$is>::from_bits(other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for $for {
|
||||
type Output = Self;
|
||||
impl Neg for $for {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self {
|
||||
$for(self.0 ^ $sign_bit)
|
||||
}
|
||||
}
|
||||
fn neg(self) -> Self {
|
||||
$for(self.0 ^ $sign_bit)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<$for> + Copy> PartialEq<T> for $for {
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
$is::from(*self) == $is::from((*other).into())
|
||||
}
|
||||
}
|
||||
impl<T: Into<$for> + Copy> PartialEq<T> for $for {
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
$is::from(*self) == $is::from((*other).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<$for> + Copy> PartialOrd<T> for $for {
|
||||
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
|
||||
$is::from(*self).partial_cmp(&$is::from((*other).into()))
|
||||
}
|
||||
}
|
||||
impl<T: Into<$for> + Copy> PartialOrd<T> for $for {
|
||||
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
|
||||
$is::from(*self).partial_cmp(&$is::from((*other).into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::fmt::Debug for $for {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
$is::from(*self).fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ::core::fmt::Debug for $for {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
$is::from(*self).fmt(f)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
float!(F32, u32, f32);
|
||||
float!(F64, u64, f64);
|
||||
|
||||
impl From<u32> for F32 {
|
||||
fn from(other: u32) -> Self {
|
||||
Self::from_bits(other)
|
||||
}
|
||||
fn from(other: u32) -> Self {
|
||||
Self::from_bits(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<F32> for u32 {
|
||||
fn from(other: F32) -> Self {
|
||||
other.to_bits()
|
||||
}
|
||||
fn from(other: F32) -> Self {
|
||||
other.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for F64 {
|
||||
fn from(other: u64) -> Self {
|
||||
Self::from_bits(other)
|
||||
}
|
||||
fn from(other: u64) -> Self {
|
||||
Self::from_bits(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<F64> for u64 {
|
||||
fn from(other: F64) -> Self {
|
||||
other.to_bits()
|
||||
}
|
||||
fn from(other: F64) -> Self {
|
||||
other.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate rand;
|
||||
extern crate rand;
|
||||
|
||||
use self::rand::Rng;
|
||||
use self::rand::Rng;
|
||||
|
||||
use super::{F32, F64};
|
||||
use super::{F32, F64};
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::fmt::Debug;
|
||||
use core::iter;
|
||||
use core::fmt::Debug;
|
||||
use core::iter;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
|
||||
fn test_ops<T, F, I>(iter: I)
|
||||
where
|
||||
T: Add<Output = T>
|
||||
+ Div<Output = T>
|
||||
+ Mul<Output = T>
|
||||
+ Sub<Output = T>
|
||||
+ Neg<Output = T>
|
||||
+ Copy
|
||||
+ Debug
|
||||
+ PartialEq,
|
||||
F: Into<T>
|
||||
+ Add<Output = F>
|
||||
+ Div<Output = F>
|
||||
+ Mul<Output = F>
|
||||
+ Sub<Output = F>
|
||||
+ Neg<Output = F>
|
||||
+ Copy
|
||||
+ Debug,
|
||||
I: IntoIterator<Item = (F, F)>,
|
||||
{
|
||||
for (a, b) in iter {
|
||||
assert_eq!((a + b).into(), a.into() + b.into());
|
||||
assert_eq!((a - b).into(), a.into() - b.into());
|
||||
assert_eq!((a * b).into(), a.into() * b.into());
|
||||
assert_eq!((a / b).into(), a.into() / b.into());
|
||||
assert_eq!((-a).into(), -a.into());
|
||||
assert_eq!((-b).into(), -b.into());
|
||||
}
|
||||
}
|
||||
fn test_ops<T, F, I>(iter: I)
|
||||
where
|
||||
T: Add<Output = T>
|
||||
+ Div<Output = T>
|
||||
+ Mul<Output = T>
|
||||
+ Sub<Output = T>
|
||||
+ Neg<Output = T>
|
||||
+ Copy
|
||||
+ Debug
|
||||
+ PartialEq,
|
||||
F: Into<T>
|
||||
+ Add<Output = F>
|
||||
+ Div<Output = F>
|
||||
+ Mul<Output = F>
|
||||
+ Sub<Output = F>
|
||||
+ Neg<Output = F>
|
||||
+ Copy
|
||||
+ Debug,
|
||||
I: IntoIterator<Item = (F, F)>,
|
||||
{
|
||||
for (a, b) in iter {
|
||||
assert_eq!((a + b).into(), a.into() + b.into());
|
||||
assert_eq!((a - b).into(), a.into() - b.into());
|
||||
assert_eq!((a * b).into(), a.into() * b.into());
|
||||
assert_eq!((a / b).into(), a.into() / b.into());
|
||||
assert_eq!((-a).into(), -a.into());
|
||||
assert_eq!((-b).into(), -b.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_f32() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let iter = iter::repeat(()).map(|_| rng.gen());
|
||||
#[test]
|
||||
fn test_ops_f32() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let iter = iter::repeat(()).map(|_| rng.gen());
|
||||
|
||||
test_ops::<F32, f32, _>(iter.take(1000));
|
||||
}
|
||||
test_ops::<F32, f32, _>(iter.take(1000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_f64() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let iter = iter::repeat(()).map(|_| rng.gen());
|
||||
#[test]
|
||||
fn test_ops_f64() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let iter = iter::repeat(()).map(|_| rng.gen());
|
||||
|
||||
test_ops::<F64, f64, _>(iter.take(1000));
|
||||
}
|
||||
test_ops::<F64, f64, _>(iter.take(1000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg_nan_f32() {
|
||||
assert_eq!((-F32(0xff80_3210)).0, 0x7f80_3210);
|
||||
}
|
||||
#[test]
|
||||
fn test_neg_nan_f32() {
|
||||
assert_eq!((-F32(0xff80_3210)).0, 0x7f80_3210);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg_nan_f64() {
|
||||
assert_eq!((-F64(0xff80_3210_0000_0000)).0, 0x7f80_3210_0000_0000);
|
||||
}
|
||||
#[test]
|
||||
fn test_neg_nan_f64() {
|
||||
assert_eq!((-F64(0xff80_3210_0000_0000)).0, 0x7f80_3210_0000_0000);
|
||||
}
|
||||
}
|
||||
|
|
33
src/table.rs
33
src/table.rs
|
@ -1,13 +1,13 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::u32;
|
||||
use core::fmt;
|
||||
use core::cell::RefCell;
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
use core::fmt;
|
||||
use core::u32;
|
||||
use func::FuncRef;
|
||||
use module::check_limits;
|
||||
use parity_wasm::elements::ResizableLimits;
|
||||
use Error;
|
||||
|
||||
/// Reference to a table (See [`TableInstance`] for details).
|
||||
///
|
||||
|
@ -106,21 +106,22 @@ impl TableInstance {
|
|||
pub fn grow(&self, by: u32) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let maximum_size = self.maximum_size().unwrap_or(u32::MAX);
|
||||
let new_size = self.current_size().checked_add(by)
|
||||
let new_size = self
|
||||
.current_size()
|
||||
.checked_add(by)
|
||||
.and_then(|new_size| {
|
||||
if maximum_size < new_size {
|
||||
None
|
||||
} else {
|
||||
Some(new_size)
|
||||
}
|
||||
})
|
||||
.ok_or_else(||
|
||||
}).ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"Trying to grow table by {} items when there are already {} items",
|
||||
by,
|
||||
self.current_size(),
|
||||
))
|
||||
)?;
|
||||
})?;
|
||||
buffer.resize(new_size as usize, None);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -129,13 +130,12 @@ impl TableInstance {
|
|||
pub fn get(&self, offset: u32) -> Result<Option<FuncRef>, Error> {
|
||||
let buffer = self.buffer.borrow();
|
||||
let buffer_len = buffer.len();
|
||||
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(||
|
||||
let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"trying to read table item with index {} when there are only {} items",
|
||||
offset,
|
||||
buffer_len
|
||||
)),
|
||||
)?;
|
||||
offset, buffer_len
|
||||
))
|
||||
})?;
|
||||
Ok(table_elem)
|
||||
}
|
||||
|
||||
|
@ -143,13 +143,12 @@ impl TableInstance {
|
|||
pub fn set(&self, offset: u32, value: Option<FuncRef>) -> Result<(), Error> {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
let buffer_len = buffer.len();
|
||||
let table_elem = buffer.get_mut(offset as usize).ok_or_else(||
|
||||
let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| {
|
||||
Error::Table(format!(
|
||||
"trying to update table item with index {} when there are only {} items",
|
||||
offset,
|
||||
buffer_len
|
||||
offset, buffer_len
|
||||
))
|
||||
)?;
|
||||
})?;
|
||||
*table_elem = value;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use {
|
||||
Error, Signature, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder,
|
||||
MemoryInstance, MemoryRef, TableInstance, TableRef, ModuleImportResolver, ModuleInstance, ModuleRef,
|
||||
RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, ResumableError,
|
||||
};
|
||||
use types::ValueType;
|
||||
use memory_units::Pages;
|
||||
use super::parse_wat;
|
||||
use memory_units::Pages;
|
||||
use types::ValueType;
|
||||
use {
|
||||
Error, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, ResumableError,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap, TrapKind,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct HostErrorWithCode {
|
||||
|
@ -107,9 +107,10 @@ impl Externals for TestHost {
|
|||
INC_MEM_FUNC_INDEX => {
|
||||
let ptr: u32 = args.nth(0);
|
||||
|
||||
let memory = self.memory.as_ref().expect(
|
||||
"Function 'inc_mem' expects attached memory",
|
||||
);
|
||||
let memory = self
|
||||
.memory
|
||||
.as_ref()
|
||||
.expect("Function 'inc_mem' expects attached memory");
|
||||
let mut buf = [0u8; 1];
|
||||
memory.get_into(ptr, &mut buf).unwrap();
|
||||
buf[0] += 1;
|
||||
|
@ -120,18 +121,22 @@ impl Externals for TestHost {
|
|||
GET_MEM_FUNC_INDEX => {
|
||||
let ptr: u32 = args.nth(0);
|
||||
|
||||
let memory = self.memory.as_ref().expect(
|
||||
"Function 'get_mem' expects attached memory",
|
||||
);
|
||||
let memory = self
|
||||
.memory
|
||||
.as_ref()
|
||||
.expect("Function 'get_mem' expects attached memory");
|
||||
let mut buf = [0u8; 1];
|
||||
memory.get_into(ptr, &mut buf).unwrap();
|
||||
|
||||
Ok(Some(RuntimeValue::I32(buf[0] as i32)))
|
||||
}
|
||||
RECURSE_FUNC_INDEX => {
|
||||
let val = args.nth_value_checked(0).expect("Exactly one argument expected");
|
||||
let val = args
|
||||
.nth_value_checked(0)
|
||||
.expect("Exactly one argument expected");
|
||||
|
||||
let instance = self.instance
|
||||
let instance = self
|
||||
.instance
|
||||
.as_ref()
|
||||
.expect("Function 'recurse' expects attached module instance")
|
||||
.clone();
|
||||
|
@ -141,7 +146,9 @@ impl Externals for TestHost {
|
|||
.expect("expected to be Some");
|
||||
|
||||
if val.value_type() != result.value_type() {
|
||||
return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into());
|
||||
return Err(
|
||||
TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into(),
|
||||
);
|
||||
}
|
||||
Ok(Some(result))
|
||||
}
|
||||
|
@ -192,17 +199,17 @@ impl ModuleImportResolver for TestHost {
|
|||
"recurse" => RECURSE_FUNC_INDEX,
|
||||
"trap_sub" => TRAP_SUB_FUNC_INDEX,
|
||||
_ => {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
if !self.check_signature(index, signature) {
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export `{}` doesnt match expected type {:?}",
|
||||
field_name,
|
||||
signature
|
||||
field_name, signature
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -214,9 +221,10 @@ impl ModuleImportResolver for TestHost {
|
|||
field_name: &str,
|
||||
_memory_type: &MemoryDescriptor,
|
||||
) -> Result<MemoryRef, Error> {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,9 +252,9 @@ fn call_host_func() {
|
|||
.assert_no_start();
|
||||
|
||||
assert_eq!(
|
||||
instance.invoke_export("test", &[], &mut env).expect(
|
||||
"Failed to invoke 'test' function",
|
||||
),
|
||||
instance
|
||||
.invoke_export("test", &[], &mut env)
|
||||
.expect("Failed to invoke 'test' function",),
|
||||
Some(RuntimeValue::I32(-2))
|
||||
);
|
||||
}
|
||||
|
@ -280,16 +288,16 @@ fn resume_call_host_func() {
|
|||
let mut invocation = FuncInstance::invoke_resumable(&func_instance, &[]).unwrap();
|
||||
let result = invocation.start_execution(&mut env);
|
||||
match result {
|
||||
Err(ResumableError::Trap(_)) => {},
|
||||
Err(ResumableError::Trap(_)) => {}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
assert!(invocation.is_resumable());
|
||||
let trap_sub_result = env.trap_sub_result.take();
|
||||
assert_eq!(
|
||||
invocation.resume_execution(trap_sub_result, &mut env).expect(
|
||||
"Failed to invoke 'test' function",
|
||||
),
|
||||
invocation
|
||||
.resume_execution(trap_sub_result, &mut env)
|
||||
.expect("Failed to invoke 'test' function",),
|
||||
Some(RuntimeValue::I32(-2))
|
||||
);
|
||||
}
|
||||
|
@ -316,13 +324,15 @@ fn host_err() {
|
|||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let error = instance.invoke_export("test", &[], &mut env).expect_err(
|
||||
"`test` expected to return error",
|
||||
);
|
||||
let error = instance
|
||||
.invoke_export("test", &[], &mut env)
|
||||
.expect_err("`test` expected to return error");
|
||||
|
||||
let error_with_code = error.as_host_error().expect("Expected host error").downcast_ref::<HostErrorWithCode>().expect(
|
||||
"Failed to downcast to expected error type",
|
||||
);
|
||||
let error_with_code = error
|
||||
.as_host_error()
|
||||
.expect("Expected host error")
|
||||
.downcast_ref::<HostErrorWithCode>()
|
||||
.expect("Failed to downcast to expected error type");
|
||||
assert_eq!(error_with_code.error_code, 228);
|
||||
}
|
||||
|
||||
|
@ -351,9 +361,9 @@ fn modify_mem_with_host_funcs() {
|
|||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
instance.invoke_export("modify_mem", &[], &mut env).expect(
|
||||
"Failed to invoke 'test' function",
|
||||
);
|
||||
instance
|
||||
.invoke_export("modify_mem", &[], &mut env)
|
||||
.expect("Failed to invoke 'test' function");
|
||||
|
||||
// Check contents of memory at address 12.
|
||||
let mut buf = [0u8; 1];
|
||||
|
@ -451,9 +461,9 @@ fn recursion() {
|
|||
env.instance = Some(instance.clone());
|
||||
|
||||
assert_eq!(
|
||||
instance.invoke_export("test", &[], &mut env).expect(
|
||||
"Failed to invoke 'test' function",
|
||||
),
|
||||
instance
|
||||
.invoke_export("test", &[], &mut env)
|
||||
.expect("Failed to invoke 'test' function",),
|
||||
// 363 = 321 + 42
|
||||
Some(RuntimeValue::I64(363))
|
||||
);
|
||||
|
@ -472,21 +482,17 @@ fn defer_providing_externals() {
|
|||
}
|
||||
|
||||
impl ModuleImportResolver for HostImportResolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
if field_name != "inc" {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
));
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)));
|
||||
}
|
||||
if signature.params() != &[ValueType::I32] || signature.return_type() != None {
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export `{}` doesnt match expected type {:?}",
|
||||
field_name,
|
||||
signature
|
||||
field_name, signature
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -501,9 +507,10 @@ fn defer_providing_externals() {
|
|||
if field_name == "mem" {
|
||||
Ok(self.mem.clone())
|
||||
} else {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -547,15 +554,16 @@ fn defer_providing_externals() {
|
|||
|
||||
// Create HostImportResolver with some initialized memory instance.
|
||||
// This memory instance will be provided as 'mem' export.
|
||||
let host_import_resolver =
|
||||
HostImportResolver { mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap() };
|
||||
let host_import_resolver = HostImportResolver {
|
||||
mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap(),
|
||||
};
|
||||
|
||||
// Instantiate module with `host_import_resolver` as import resolver for "host" module.
|
||||
let instance = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::new().with_resolver("host", &host_import_resolver),
|
||||
).expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
.assert_no_start();
|
||||
|
||||
let mut acc = 89;
|
||||
{
|
||||
|
@ -599,18 +607,15 @@ fn two_envs_one_externals() {
|
|||
struct OrdinaryResolver;
|
||||
|
||||
impl ModuleImportResolver for PrivilegedResolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
let index = match field_name {
|
||||
"ordinary" => ORDINARY_FUNC_INDEX,
|
||||
"privileged" => PRIVILEGED_FUNC_INDEX,
|
||||
_ => {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -619,11 +624,7 @@ fn two_envs_one_externals() {
|
|||
}
|
||||
|
||||
impl ModuleImportResolver for OrdinaryResolver {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
let index = match field_name {
|
||||
"ordinary" => ORDINARY_FUNC_INDEX,
|
||||
"privileged" => {
|
||||
|
@ -632,9 +633,10 @@ fn two_envs_one_externals() {
|
|||
))
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -674,7 +676,7 @@ fn two_envs_one_externals() {
|
|||
&trusted_module,
|
||||
&ImportsBuilder::new().with_resolver("env", &PrivilegedResolver),
|
||||
).expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
.assert_no_start();
|
||||
|
||||
let untrusted_instance = ModuleInstance::new(
|
||||
&untrusted_module,
|
||||
|
@ -682,7 +684,7 @@ fn two_envs_one_externals() {
|
|||
.with_resolver("env", &OrdinaryResolver)
|
||||
.with_resolver("trusted", &trusted_instance),
|
||||
).expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
.assert_no_start();
|
||||
|
||||
untrusted_instance
|
||||
.invoke_export("test", &[], &mut HostExternals)
|
||||
|
@ -716,7 +718,8 @@ fn dynamically_add_host_func() {
|
|||
Signature::new(&[][..], Some(ValueType::I32)),
|
||||
host_func_index as usize,
|
||||
);
|
||||
self.table.set(table_index, Some(added_func))
|
||||
self.table
|
||||
.set(table_index, Some(added_func))
|
||||
.map_err(|_| TrapKind::TableAccessOutOfBounds)?;
|
||||
|
||||
Ok(Some(RuntimeValue::I32(table_index as i32)))
|
||||
|
@ -730,17 +733,14 @@ fn dynamically_add_host_func() {
|
|||
}
|
||||
|
||||
impl ModuleImportResolver for HostExternals {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<FuncRef, Error> {
|
||||
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
|
||||
let index = match field_name {
|
||||
"add_func" => ADD_FUNC_FUNC_INDEX,
|
||||
_ => {
|
||||
return Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
return Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
};
|
||||
Ok(FuncInstance::alloc_host(signature.clone(), index))
|
||||
|
@ -754,9 +754,10 @@ fn dynamically_add_host_func() {
|
|||
if field_name == "table" {
|
||||
Ok(self.table.clone())
|
||||
} else {
|
||||
Err(Error::Instantiation(
|
||||
format!("Export {} not found", field_name),
|
||||
))
|
||||
Err(Error::Instantiation(format!(
|
||||
"Export {} not found",
|
||||
field_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -789,7 +790,7 @@ fn dynamically_add_host_func() {
|
|||
&module,
|
||||
&ImportsBuilder::new().with_resolver("env", &host_externals),
|
||||
).expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
.assert_no_start();
|
||||
|
||||
assert_eq!(
|
||||
instance
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use wabt;
|
||||
use {Module};
|
||||
use Module;
|
||||
|
||||
mod host;
|
||||
mod wasm;
|
||||
|
@ -24,10 +24,16 @@ fn unsigned_to_runtime_value() {
|
|||
use super::RuntimeValue;
|
||||
|
||||
let overflow_i32: u32 = ::core::i32::MAX as u32 + 1;
|
||||
assert_eq!(RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(), overflow_i32);
|
||||
assert_eq!(
|
||||
RuntimeValue::from(overflow_i32).try_into::<u32>().unwrap(),
|
||||
overflow_i32
|
||||
);
|
||||
|
||||
let overflow_i64: u64 = ::core::i64::MAX as u64 + 1;
|
||||
assert_eq!(RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(), overflow_i64);
|
||||
assert_eq!(
|
||||
RuntimeValue::from(overflow_i64).try_into::<u64>().unwrap(),
|
||||
overflow_i64
|
||||
);
|
||||
}
|
||||
|
||||
pub fn parse_wat(source: &str) -> Module {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use {
|
||||
Error, Signature, FuncRef, GlobalInstance, GlobalRef, ImportsBuilder, MemoryInstance,
|
||||
MemoryRef, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue,
|
||||
TableInstance, TableRef, Module, GlobalDescriptor, TableDescriptor, MemoryDescriptor,
|
||||
};
|
||||
use memory_units::Pages;
|
||||
use std::fs::File;
|
||||
use {
|
||||
Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals,
|
||||
RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef,
|
||||
};
|
||||
|
||||
struct Env {
|
||||
table_base: GlobalRef,
|
||||
|
@ -60,12 +60,17 @@ impl ModuleImportResolver for Env {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> {
|
||||
fn resolve_table(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, Error> {
|
||||
match field_name {
|
||||
"table" => Ok(self.table.clone()),
|
||||
_ => Err(Error::Instantiation(
|
||||
format!("env module doesn't provide table '{}'", field_name),
|
||||
)),
|
||||
_ => Err(Error::Instantiation(format!(
|
||||
"env module doesn't provide table '{}'",
|
||||
field_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,10 +95,8 @@ fn interpreter_inc_i32() {
|
|||
|
||||
let env = Env::new();
|
||||
|
||||
let instance = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::new().with_resolver("env", &env),
|
||||
).expect("Failed to instantiate module")
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let i32_val = 42;
|
||||
|
@ -109,40 +112,40 @@ fn interpreter_inc_i32() {
|
|||
|
||||
#[test]
|
||||
fn interpreter_accumulate_u8() {
|
||||
// Name of function contained in WASM file (note the leading underline)
|
||||
const FUNCTION_NAME: &'static str = "_accumulate_u8";
|
||||
// The WASM file containing the module and function
|
||||
const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast";
|
||||
// The octet sequence being accumulated
|
||||
const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1];
|
||||
// Name of function contained in WASM file (note the leading underline)
|
||||
const FUNCTION_NAME: &'static str = "_accumulate_u8";
|
||||
// The WASM file containing the module and function
|
||||
const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast";
|
||||
// The octet sequence being accumulated
|
||||
const BUF: &[u8] = &[9, 8, 7, 6, 5, 4, 3, 2, 1];
|
||||
|
||||
|
||||
// Load the module-structure from wasm-file and add to program
|
||||
// Load the module-structure from wasm-file and add to program
|
||||
let module = load_from_file(WASM_FILE);
|
||||
|
||||
let env = Env::new();
|
||||
let instance = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::new().with_resolver("env", &env),
|
||||
).expect("Failed to instantiate module")
|
||||
let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
|
||||
.expect("Failed to instantiate module")
|
||||
.assert_no_start();
|
||||
|
||||
let env_memory = env.memory.clone();
|
||||
let env_memory = env.memory.clone();
|
||||
|
||||
// Place the octet-sequence at index 0 in linear memory
|
||||
let offset: u32 = 0;
|
||||
let _ = env_memory.set(offset, BUF);
|
||||
// Place the octet-sequence at index 0 in linear memory
|
||||
let offset: u32 = 0;
|
||||
let _ = env_memory.set(offset, BUF);
|
||||
|
||||
// Set up the function argument list and invoke the function
|
||||
let args = &[RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)];
|
||||
let retval = instance
|
||||
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
|
||||
.expect("Failed to execute function");
|
||||
// Set up the function argument list and invoke the function
|
||||
let args = &[
|
||||
RuntimeValue::I32(BUF.len() as i32),
|
||||
RuntimeValue::I32(offset as i32),
|
||||
];
|
||||
let retval = instance
|
||||
.invoke_export(FUNCTION_NAME, args, &mut NopExternals)
|
||||
.expect("Failed to execute function");
|
||||
|
||||
// For verification, repeat accumulation using native code
|
||||
let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32);
|
||||
let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu));
|
||||
// For verification, repeat accumulation using native code
|
||||
let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32);
|
||||
let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu));
|
||||
|
||||
// Verify calculation from WebAssembly runtime is identical to expected result
|
||||
assert_eq!(exp_retval, retval);
|
||||
// Verify calculation from WebAssembly runtime is identical to expected result
|
||||
assert_eq!(exp_retval, retval);
|
||||
}
|
||||
|
|
12
src/types.rs
12
src/types.rs
|
@ -1,7 +1,8 @@
|
|||
use alloc::borrow::Cow;
|
||||
|
||||
use parity_wasm::elements::{
|
||||
FunctionType, ValueType as EValueType, GlobalType, TableType, MemoryType};
|
||||
FunctionType, GlobalType, MemoryType, TableType, ValueType as EValueType,
|
||||
};
|
||||
|
||||
/// Signature of a [function].
|
||||
///
|
||||
|
@ -38,7 +39,7 @@ impl Signature {
|
|||
/// ```
|
||||
pub fn new<C: Into<Cow<'static, [ValueType]>>>(
|
||||
params: C,
|
||||
return_type: Option<ValueType>
|
||||
return_type: Option<ValueType>,
|
||||
) -> Signature {
|
||||
Signature {
|
||||
params: params.into(),
|
||||
|
@ -58,7 +59,12 @@ impl Signature {
|
|||
|
||||
pub(crate) fn from_elements(func_type: &FunctionType) -> Signature {
|
||||
Signature {
|
||||
params: func_type.params().iter().cloned().map(ValueType::from_elements).collect(),
|
||||
params: func_type
|
||||
.params()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(ValueType::from_elements)
|
||||
.collect(),
|
||||
return_type: func_type.return_type().map(ValueType::from_elements),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use parity_wasm::elements::{MemoryType, TableType, GlobalType, BlockType, ValueType, FunctionType};
|
||||
use parity_wasm::elements::{
|
||||
BlockType, FunctionType, GlobalType, MemoryType, TableType, ValueType,
|
||||
};
|
||||
use validation::Error;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -47,26 +49,30 @@ impl ModuleContext {
|
|||
}
|
||||
|
||||
pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
|
||||
let ty_idx = self.func_type_indexes()
|
||||
let ty_idx = self
|
||||
.func_type_indexes()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?;
|
||||
self.require_function_type(*ty_idx)
|
||||
}
|
||||
|
||||
pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
|
||||
let ty = self.types()
|
||||
let ty = self
|
||||
.types()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?;
|
||||
|
||||
let params = ty.params();
|
||||
let return_ty = ty.return_type()
|
||||
let return_ty = ty
|
||||
.return_type()
|
||||
.map(BlockType::Value)
|
||||
.unwrap_or(BlockType::NoResult);
|
||||
Ok((params, return_ty))
|
||||
}
|
||||
|
||||
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<&GlobalType, Error> {
|
||||
let global = self.globals()
|
||||
let global = self
|
||||
.globals()
|
||||
.get(idx as usize)
|
||||
.ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?;
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use core::u32;
|
||||
use parity_wasm::elements::{Instruction, BlockType, ValueType, TableElementType, Func, FuncBody};
|
||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||
use core::u32;
|
||||
use parity_wasm::elements::{BlockType, Func, FuncBody, Instruction, TableElementType, ValueType};
|
||||
use validation::context::ModuleContext;
|
||||
|
||||
use validation::Error;
|
||||
use validation::util::Locals;
|
||||
use validation::Error;
|
||||
|
||||
use common::stack::StackWithLimit;
|
||||
use isa;
|
||||
|
@ -39,13 +39,9 @@ enum BlockFrameType {
|
|||
/// Usual block frame.
|
||||
///
|
||||
/// Can be used for an implicit function block.
|
||||
Block {
|
||||
end_label: LabelId,
|
||||
},
|
||||
Block { end_label: LabelId },
|
||||
/// Loop frame (branching to the beginning of block).
|
||||
Loop {
|
||||
header: LabelId,
|
||||
},
|
||||
Loop { header: LabelId },
|
||||
/// True-subblock of if expression.
|
||||
IfTrue {
|
||||
/// If jump happens inside the if-true block then control will
|
||||
|
@ -58,9 +54,7 @@ enum BlockFrameType {
|
|||
if_not: LabelId,
|
||||
},
|
||||
/// False-subblock of if expression.
|
||||
IfFalse {
|
||||
end_label: LabelId,
|
||||
}
|
||||
IfFalse { end_label: LabelId },
|
||||
}
|
||||
|
||||
impl BlockFrameType {
|
||||
|
@ -183,9 +177,7 @@ impl FunctionReader {
|
|||
|
||||
let end_label = context.sink.new_label();
|
||||
push_label(
|
||||
BlockFrameType::Block {
|
||||
end_label,
|
||||
},
|
||||
BlockFrameType::Block { end_label },
|
||||
result_ty,
|
||||
context.position,
|
||||
&context.value_stack,
|
||||
|
@ -198,7 +190,10 @@ impl FunctionReader {
|
|||
Ok(context.into_code())
|
||||
}
|
||||
|
||||
fn read_function_body(context: &mut FunctionValidationContext, body: &[Instruction]) -> Result<(), Error> {
|
||||
fn read_function_body(
|
||||
context: &mut FunctionValidationContext,
|
||||
body: &[Instruction],
|
||||
) -> Result<(), Error> {
|
||||
let body_len = body.len();
|
||||
if body_len == 0 {
|
||||
return Err(Error("Non-empty function body expected".into()));
|
||||
|
@ -207,15 +202,19 @@ impl FunctionReader {
|
|||
loop {
|
||||
let instruction = &body[context.position];
|
||||
|
||||
let outcome = FunctionReader::read_instruction(context, instruction)
|
||||
.map_err(|err| Error(format!("At instruction {:?}(@{}): {}", instruction, context.position, err)))?;
|
||||
let outcome =
|
||||
FunctionReader::read_instruction(context, instruction).map_err(|err| {
|
||||
Error(format!(
|
||||
"At instruction {:?}(@{}): {}",
|
||||
instruction, context.position, err
|
||||
))
|
||||
})?;
|
||||
|
||||
match outcome {
|
||||
Outcome::NextInstruction => (),
|
||||
Outcome::Unreachable => make_top_frame_polymorphic(
|
||||
&mut context.value_stack,
|
||||
&mut context.frame_stack
|
||||
),
|
||||
Outcome::Unreachable => {
|
||||
make_top_frame_polymorphic(&mut context.value_stack, &mut context.frame_stack)
|
||||
}
|
||||
}
|
||||
|
||||
context.position += 1;
|
||||
|
@ -225,11 +224,14 @@ impl FunctionReader {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_instruction(context: &mut FunctionValidationContext, instruction: &Instruction) -> Result<Outcome, Error> {
|
||||
fn read_instruction(
|
||||
context: &mut FunctionValidationContext,
|
||||
instruction: &Instruction,
|
||||
) -> Result<Outcome, Error> {
|
||||
use self::Instruction::*;
|
||||
match *instruction {
|
||||
// Nop instruction doesn't do anything. It is safe to just skip it.
|
||||
Nop => {},
|
||||
Nop => {}
|
||||
|
||||
Unreachable => {
|
||||
context.sink.emit(isa::Instruction::Unreachable);
|
||||
|
@ -239,9 +241,7 @@ impl FunctionReader {
|
|||
Block(block_type) => {
|
||||
let end_label = context.sink.new_label();
|
||||
push_label(
|
||||
BlockFrameType::Block {
|
||||
end_label
|
||||
},
|
||||
BlockFrameType::Block { end_label },
|
||||
block_type,
|
||||
context.position,
|
||||
&context.value_stack,
|
||||
|
@ -254,9 +254,7 @@ impl FunctionReader {
|
|||
context.sink.resolve_label(header);
|
||||
|
||||
push_label(
|
||||
BlockFrameType::Loop {
|
||||
header,
|
||||
},
|
||||
BlockFrameType::Loop { header },
|
||||
block_type,
|
||||
context.position,
|
||||
&context.value_stack,
|
||||
|
@ -269,12 +267,13 @@ impl FunctionReader {
|
|||
let if_not = context.sink.new_label();
|
||||
let end_label = context.sink.new_label();
|
||||
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
ValueType::I32.into(),
|
||||
)?;
|
||||
push_label(
|
||||
BlockFrameType::IfTrue {
|
||||
if_not,
|
||||
end_label,
|
||||
},
|
||||
BlockFrameType::IfTrue { if_not, end_label },
|
||||
block_type,
|
||||
context.position,
|
||||
&context.value_stack,
|
||||
|
@ -283,14 +282,15 @@ impl FunctionReader {
|
|||
|
||||
context.sink.emit_br_eqz(Target {
|
||||
label: if_not,
|
||||
drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, },
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
});
|
||||
}
|
||||
Else => {
|
||||
let (block_type, if_not, end_label) = {
|
||||
let top_frame = top_label(
|
||||
&context.frame_stack,
|
||||
);
|
||||
let top_frame = top_label(&context.frame_stack);
|
||||
|
||||
let (if_not, end_label) = match top_frame.frame_type {
|
||||
BlockFrameType::IfTrue { if_not, end_label } => (if_not, end_label),
|
||||
|
@ -303,7 +303,10 @@ impl FunctionReader {
|
|||
// to the "end_label" (it will be resolved at End).
|
||||
context.sink.emit_br(Target {
|
||||
label: end_label,
|
||||
drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, },
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
});
|
||||
|
||||
// Resolve `if_not` to here so when if condition is unsatisfied control flow
|
||||
|
@ -312,14 +315,9 @@ impl FunctionReader {
|
|||
|
||||
// Then, we pop the current label. It discards all values that pushed in the current
|
||||
// frame.
|
||||
pop_label(
|
||||
&mut context.value_stack,
|
||||
&mut context.frame_stack
|
||||
)?;
|
||||
pop_label(&mut context.value_stack, &mut context.frame_stack)?;
|
||||
push_label(
|
||||
BlockFrameType::IfFalse {
|
||||
end_label,
|
||||
},
|
||||
BlockFrameType::IfFalse { end_label },
|
||||
block_type,
|
||||
context.position,
|
||||
&context.value_stack,
|
||||
|
@ -335,14 +333,10 @@ impl FunctionReader {
|
|||
if let BlockFrameType::IfTrue { if_not, .. } = frame_type {
|
||||
// A `if` without an `else` can't return a result.
|
||||
if block_type != BlockType::NoResult {
|
||||
return Err(
|
||||
Error(
|
||||
format!(
|
||||
return Err(Error(format!(
|
||||
"If block without else required to have NoResult block type. But it has {:?} type",
|
||||
block_type
|
||||
)
|
||||
)
|
||||
);
|
||||
)));
|
||||
}
|
||||
|
||||
// Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump
|
||||
|
@ -365,7 +359,7 @@ impl FunctionReader {
|
|||
tee_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into()
|
||||
value_type.into(),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
@ -388,11 +382,7 @@ impl FunctionReader {
|
|||
Br(depth) => {
|
||||
Validator::validate_br(context, depth)?;
|
||||
|
||||
let target = require_target(
|
||||
depth,
|
||||
&context.value_stack,
|
||||
&context.frame_stack,
|
||||
);
|
||||
let target = require_target(depth, &context.value_stack, &context.frame_stack);
|
||||
context.sink.emit_br(target);
|
||||
|
||||
return Ok(Outcome::Unreachable);
|
||||
|
@ -400,11 +390,7 @@ impl FunctionReader {
|
|||
BrIf(depth) => {
|
||||
Validator::validate_br_if(context, depth)?;
|
||||
|
||||
let target = require_target(
|
||||
depth,
|
||||
&context.value_stack,
|
||||
&context.frame_stack,
|
||||
);
|
||||
let target = require_target(depth, &context.value_stack, &context.frame_stack);
|
||||
context.sink.emit_br_nez(target);
|
||||
}
|
||||
BrTable(ref table, default) => {
|
||||
|
@ -412,32 +398,26 @@ impl FunctionReader {
|
|||
|
||||
let mut targets = Vec::new();
|
||||
for depth in table.iter() {
|
||||
let target = require_target(
|
||||
*depth,
|
||||
&context.value_stack,
|
||||
&context.frame_stack,
|
||||
);
|
||||
let target = require_target(*depth, &context.value_stack, &context.frame_stack);
|
||||
targets.push(target);
|
||||
}
|
||||
let default_target = require_target(
|
||||
default,
|
||||
&context.value_stack,
|
||||
&context.frame_stack,
|
||||
);
|
||||
let default_target =
|
||||
require_target(default, &context.value_stack, &context.frame_stack);
|
||||
context.sink.emit_br_table(&targets, default_target);
|
||||
|
||||
return Ok(Outcome::Unreachable);
|
||||
}
|
||||
Return => {
|
||||
if let BlockType::Value(value_type) = context.return_type()? {
|
||||
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
tee_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
}
|
||||
|
||||
let drop_keep = drop_keep_return(
|
||||
&context.locals,
|
||||
&context.value_stack,
|
||||
&context.frame_stack,
|
||||
);
|
||||
let drop_keep =
|
||||
drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack);
|
||||
context.sink.emit(isa::Instruction::Return(drop_keep));
|
||||
|
||||
return Ok(Outcome::Unreachable);
|
||||
|
@ -464,37 +444,19 @@ impl FunctionReader {
|
|||
GetLocal(index) => {
|
||||
// We need to calculate relative depth before validation since
|
||||
// it will change the value stack size.
|
||||
let depth = relative_local_depth(
|
||||
index,
|
||||
&context.locals,
|
||||
&context.value_stack,
|
||||
)?;
|
||||
let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
|
||||
Validator::validate_get_local(context, index)?;
|
||||
context.sink.emit(
|
||||
isa::Instruction::GetLocal(depth),
|
||||
);
|
||||
context.sink.emit(isa::Instruction::GetLocal(depth));
|
||||
}
|
||||
SetLocal(index) => {
|
||||
Validator::validate_set_local(context, index)?;
|
||||
let depth = relative_local_depth(
|
||||
index,
|
||||
&context.locals,
|
||||
&context.value_stack,
|
||||
)?;
|
||||
context.sink.emit(
|
||||
isa::Instruction::SetLocal(depth),
|
||||
);
|
||||
let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
|
||||
context.sink.emit(isa::Instruction::SetLocal(depth));
|
||||
}
|
||||
TeeLocal(index) => {
|
||||
Validator::validate_tee_local(context, index)?;
|
||||
let depth = relative_local_depth(
|
||||
index,
|
||||
&context.locals,
|
||||
&context.value_stack,
|
||||
)?;
|
||||
context.sink.emit(
|
||||
isa::Instruction::TeeLocal(depth),
|
||||
);
|
||||
let depth = relative_local_depth(index, &context.locals, &context.value_stack)?;
|
||||
context.sink.emit(isa::Instruction::TeeLocal(depth));
|
||||
}
|
||||
GetGlobal(index) => {
|
||||
Validator::validate_get_global(context, index)?;
|
||||
|
@ -1136,78 +1098,160 @@ impl FunctionReader {
|
|||
struct Validator;
|
||||
|
||||
impl Validator {
|
||||
fn validate_const(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||
fn validate_const(
|
||||
context: &mut FunctionValidationContext,
|
||||
value_type: ValueType,
|
||||
) -> Result<(), Error> {
|
||||
push_value(&mut context.value_stack, value_type.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_unop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
fn validate_unop(
|
||||
context: &mut FunctionValidationContext,
|
||||
value_type: ValueType,
|
||||
) -> Result<(), Error> {
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
push_value(&mut context.value_stack, value_type.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_binop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
fn validate_binop(
|
||||
context: &mut FunctionValidationContext,
|
||||
value_type: ValueType,
|
||||
) -> Result<(), Error> {
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
push_value(&mut context.value_stack, value_type.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_testop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
fn validate_testop(
|
||||
context: &mut FunctionValidationContext,
|
||||
value_type: ValueType,
|
||||
) -> Result<(), Error> {
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_relop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<(), Error> {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
fn validate_relop(
|
||||
context: &mut FunctionValidationContext,
|
||||
value_type: ValueType,
|
||||
) -> Result<(), Error> {
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: ValueType, value_type2: ValueType) -> Result<(), Error> {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, value_type1.into())?;
|
||||
fn validate_cvtop(
|
||||
context: &mut FunctionValidationContext,
|
||||
value_type1: ValueType,
|
||||
value_type2: ValueType,
|
||||
) -> Result<(), Error> {
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type1.into(),
|
||||
)?;
|
||||
push_value(&mut context.value_stack, value_type2.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_drop(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
StackValueType::Any,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_select(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||
let select_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
ValueType::I32.into(),
|
||||
)?;
|
||||
let select_type = pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
StackValueType::Any,
|
||||
)?;
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, select_type)?;
|
||||
push_value(&mut context.value_stack, select_type)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||
fn validate_get_local(
|
||||
context: &mut FunctionValidationContext,
|
||||
index: u32,
|
||||
) -> Result<(), Error> {
|
||||
let local_type = require_local(&context.locals, index)?;
|
||||
push_value(&mut context.value_stack, local_type.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||
fn validate_set_local(
|
||||
context: &mut FunctionValidationContext,
|
||||
index: u32,
|
||||
) -> Result<(), Error> {
|
||||
let local_type = require_local(&context.locals, index)?;
|
||||
let value_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?;
|
||||
if StackValueType::from(local_type) != value_type {
|
||||
return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
|
||||
let value_type = pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
StackValueType::Any,
|
||||
)?;
|
||||
if StackValueType::from(local_type) != value_type {
|
||||
return Err(Error(format!(
|
||||
"Trying to update local {} of type {:?} with value of type {:?}",
|
||||
index, local_type, value_type
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||
fn validate_tee_local(
|
||||
context: &mut FunctionValidationContext,
|
||||
index: u32,
|
||||
) -> Result<(), Error> {
|
||||
let local_type = require_local(&context.locals, index)?;
|
||||
tee_value(&mut context.value_stack, &context.frame_stack, local_type.into())?;
|
||||
tee_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
local_type.into(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||
fn validate_get_global(
|
||||
context: &mut FunctionValidationContext,
|
||||
index: u32,
|
||||
) -> Result<(), Error> {
|
||||
let global_type: StackValueType = {
|
||||
let global = context.module.require_global(index, None)?;
|
||||
global.content_type().into()
|
||||
|
@ -1216,37 +1260,75 @@ impl Validator {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> {
|
||||
fn validate_set_global(
|
||||
context: &mut FunctionValidationContext,
|
||||
index: u32,
|
||||
) -> Result<(), Error> {
|
||||
let global_type: StackValueType = {
|
||||
let global = context.module.require_global(index, Some(true))?;
|
||||
global.content_type().into()
|
||||
};
|
||||
let value_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?;
|
||||
let value_type = pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
StackValueType::Any,
|
||||
)?;
|
||||
if global_type != value_type {
|
||||
return Err(Error(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type)));
|
||||
return Err(Error(format!(
|
||||
"Trying to update global {} of type {:?} with value of type {:?}",
|
||||
index, global_type, value_type
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result<(), Error> {
|
||||
fn validate_load(
|
||||
context: &mut FunctionValidationContext,
|
||||
align: u32,
|
||||
max_align: u32,
|
||||
value_type: ValueType,
|
||||
) -> Result<(), Error> {
|
||||
if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align {
|
||||
return Err(Error(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align)));
|
||||
return Err(Error(format!(
|
||||
"Too large memory alignment 2^{} (expected at most {})",
|
||||
align, max_align
|
||||
)));
|
||||
}
|
||||
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
ValueType::I32.into(),
|
||||
)?;
|
||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
push_value(&mut context.value_stack, value_type.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result<(), Error> {
|
||||
fn validate_store(
|
||||
context: &mut FunctionValidationContext,
|
||||
align: u32,
|
||||
max_align: u32,
|
||||
value_type: ValueType,
|
||||
) -> Result<(), Error> {
|
||||
if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align {
|
||||
return Err(Error(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align)));
|
||||
return Err(Error(format!(
|
||||
"Too large memory alignment 2^{} (expected at most {})",
|
||||
align, max_align
|
||||
)));
|
||||
}
|
||||
|
||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
ValueType::I32.into(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1257,14 +1339,22 @@ impl Validator {
|
|||
};
|
||||
if !frame_type.is_loop() {
|
||||
if let BlockType::Value(value_type) = frame_block_type {
|
||||
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
tee_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_br_if(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
ValueType::I32.into(),
|
||||
)?;
|
||||
|
||||
let (frame_type, frame_block_type) = {
|
||||
let frame = require_label(depth, &context.frame_stack)?;
|
||||
|
@ -1272,13 +1362,21 @@ impl Validator {
|
|||
};
|
||||
if !frame_type.is_loop() {
|
||||
if let BlockType::Value(value_type) = frame_block_type {
|
||||
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
tee_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_br_table(context: &mut FunctionValidationContext, table: &[u32], default: u32) -> Result<(), Error> {
|
||||
fn validate_br_table(
|
||||
context: &mut FunctionValidationContext,
|
||||
table: &[u32],
|
||||
default: u32,
|
||||
) -> Result<(), Error> {
|
||||
let required_block_type: BlockType = {
|
||||
let default_block = require_label(default, &context.frame_stack)?;
|
||||
let required_block_type = if !default_block.frame_type.is_loop() {
|
||||
|
@ -1295,23 +1393,26 @@ impl Validator {
|
|||
BlockType::NoResult
|
||||
};
|
||||
if required_block_type != label_block_type {
|
||||
return Err(
|
||||
Error(
|
||||
format!(
|
||||
"Labels in br_table points to block of different types: {:?} and {:?}",
|
||||
required_block_type,
|
||||
label_block.block_type
|
||||
)
|
||||
)
|
||||
);
|
||||
return Err(Error(format!(
|
||||
"Labels in br_table points to block of different types: {:?} and {:?}",
|
||||
required_block_type, label_block.block_type
|
||||
)));
|
||||
}
|
||||
}
|
||||
required_block_type
|
||||
};
|
||||
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
ValueType::I32.into(),
|
||||
)?;
|
||||
if let BlockType::Value(value_type) = required_block_type {
|
||||
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
|
||||
tee_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
value_type.into(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1320,7 +1421,11 @@ impl Validator {
|
|||
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> {
|
||||
let (argument_types, return_type) = context.module.require_function(idx)?;
|
||||
for argument_type in argument_types.iter().rev() {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, (*argument_type).into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
(*argument_type).into(),
|
||||
)?;
|
||||
}
|
||||
if let BlockType::Value(value_type) = return_type {
|
||||
push_value(&mut context.value_stack, value_type.into())?;
|
||||
|
@ -1328,7 +1433,10 @@ impl Validator {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> {
|
||||
fn validate_call_indirect(
|
||||
context: &mut FunctionValidationContext,
|
||||
idx: u32,
|
||||
) -> Result<(), Error> {
|
||||
{
|
||||
let table = context.module.require_table(DEFAULT_TABLE_INDEX)?;
|
||||
if table.elem_type() != TableElementType::AnyFunc {
|
||||
|
@ -1340,10 +1448,18 @@ impl Validator {
|
|||
}
|
||||
}
|
||||
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
ValueType::I32.into(),
|
||||
)?;
|
||||
let (argument_types, return_type) = context.module.require_function_type(idx)?;
|
||||
for argument_type in argument_types.iter().rev() {
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, (*argument_type).into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
(*argument_type).into(),
|
||||
)?;
|
||||
}
|
||||
if let BlockType::Value(value_type) = return_type {
|
||||
push_value(&mut context.value_stack, value_type.into())?;
|
||||
|
@ -1359,7 +1475,11 @@ impl Validator {
|
|||
|
||||
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<(), Error> {
|
||||
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?;
|
||||
pop_value(
|
||||
&mut context.value_stack,
|
||||
&context.frame_stack,
|
||||
ValueType::I32.into(),
|
||||
)?;
|
||||
push_value(&mut context.value_stack, ValueType::I32.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1416,7 +1536,9 @@ fn make_top_frame_polymorphic(
|
|||
value_stack: &mut StackWithLimit<StackValueType>,
|
||||
frame_stack: &mut StackWithLimit<BlockFrame>,
|
||||
) {
|
||||
let frame = frame_stack.top_mut().expect("make_top_frame_polymorphic is called with empty frame stack");
|
||||
let frame = frame_stack
|
||||
.top_mut()
|
||||
.expect("make_top_frame_polymorphic is called with empty frame stack");
|
||||
value_stack.resize(frame.value_stack_len, StackValueType::Any);
|
||||
frame.polymorphic_stack = true;
|
||||
}
|
||||
|
@ -1501,7 +1623,11 @@ fn pop_label(
|
|||
match block_type {
|
||||
BlockType::NoResult => (),
|
||||
BlockType::Value(required_value_type) => {
|
||||
let _ = pop_value(value_stack, frame_stack, StackValueType::Specific(required_value_type))?;
|
||||
let _ = pop_value(
|
||||
value_stack,
|
||||
frame_stack,
|
||||
StackValueType::Specific(required_value_type),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1518,7 +1644,8 @@ fn pop_label(
|
|||
}
|
||||
|
||||
fn top_label(frame_stack: &StackWithLimit<BlockFrame>) -> &BlockFrame {
|
||||
frame_stack.top()
|
||||
frame_stack
|
||||
.top()
|
||||
.expect("this function can't be called with empty frame stack")
|
||||
}
|
||||
|
||||
|
@ -1534,8 +1661,7 @@ fn require_target(
|
|||
value_stack: &StackWithLimit<StackValueType>,
|
||||
frame_stack: &StackWithLimit<BlockFrame>,
|
||||
) -> Target {
|
||||
let is_stack_polymorphic = top_label(frame_stack)
|
||||
.polymorphic_stack;
|
||||
let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack;
|
||||
let frame =
|
||||
require_label(depth, frame_stack).expect("require_target called with a bogus depth");
|
||||
|
||||
|
@ -1604,7 +1730,7 @@ fn require_local(locals: &Locals, idx: u32) -> Result<ValueType, Error> {
|
|||
fn relative_local_depth(
|
||||
idx: u32,
|
||||
locals: &Locals,
|
||||
value_stack: &StackWithLimit<StackValueType>
|
||||
value_stack: &StackWithLimit<StackValueType>,
|
||||
) -> Result<u32, Error> {
|
||||
let value_stack_height = value_stack.len() as u32;
|
||||
let locals_and_params_count = locals.count();
|
||||
|
@ -1612,9 +1738,7 @@ fn relative_local_depth(
|
|||
let depth = value_stack_height
|
||||
.checked_add(locals_and_params_count)
|
||||
.and_then(|x| x.checked_sub(idx))
|
||||
.ok_or_else(||
|
||||
Error(String::from("Locals range not in 32-bit range"))
|
||||
)?;
|
||||
.ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
|
||||
Ok(depth)
|
||||
}
|
||||
|
||||
|
@ -1655,12 +1779,15 @@ impl Sink {
|
|||
self.ins.current_pc()
|
||||
}
|
||||
|
||||
fn pc_or_placeholder<F: FnOnce() -> isa::Reloc>(&mut self, label: LabelId, reloc_creator: F) -> u32 {
|
||||
fn pc_or_placeholder<F: FnOnce() -> isa::Reloc>(
|
||||
&mut self,
|
||||
label: LabelId,
|
||||
reloc_creator: F,
|
||||
) -> u32 {
|
||||
match self.labels[label.0] {
|
||||
(Label::Resolved(dst_pc), _) => dst_pc,
|
||||
(Label::NotResolved, ref mut unresolved) => {
|
||||
unresolved
|
||||
.push(reloc_creator());
|
||||
unresolved.push(reloc_creator());
|
||||
u32::max_value()
|
||||
}
|
||||
}
|
||||
|
@ -1671,10 +1798,7 @@ impl Sink {
|
|||
}
|
||||
|
||||
fn emit_br(&mut self, target: Target) {
|
||||
let Target {
|
||||
label,
|
||||
drop_keep,
|
||||
} = target;
|
||||
let Target { label, drop_keep } = target;
|
||||
let pc = self.cur_pc();
|
||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
|
||||
self.ins.push(isa::Instruction::Br(isa::Target {
|
||||
|
@ -1684,10 +1808,7 @@ impl Sink {
|
|||
}
|
||||
|
||||
fn emit_br_eqz(&mut self, target: Target) {
|
||||
let Target {
|
||||
label,
|
||||
drop_keep,
|
||||
} = target;
|
||||
let Target { label, drop_keep } = target;
|
||||
let pc = self.cur_pc();
|
||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
|
||||
self.ins.push(isa::Instruction::BrIfEqz(isa::Target {
|
||||
|
@ -1697,10 +1818,7 @@ impl Sink {
|
|||
}
|
||||
|
||||
fn emit_br_nez(&mut self, target: Target) {
|
||||
let Target {
|
||||
label,
|
||||
drop_keep,
|
||||
} = target;
|
||||
let Target { label, drop_keep } = target;
|
||||
let pc = self.cur_pc();
|
||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc });
|
||||
self.ins.push(isa::Instruction::BrIfNez(isa::Target {
|
||||
|
@ -1714,26 +1832,23 @@ impl Sink {
|
|||
|
||||
let pc = self.cur_pc();
|
||||
let mut isa_targets = Vec::new();
|
||||
for (idx, &Target { label, drop_keep }) in targets.iter().chain(iter::once(&default)).enumerate() {
|
||||
for (idx, &Target { label, drop_keep }) in
|
||||
targets.iter().chain(iter::once(&default)).enumerate()
|
||||
{
|
||||
let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx });
|
||||
isa_targets.push(
|
||||
isa::Target {
|
||||
dst_pc,
|
||||
drop_keep: drop_keep.into(),
|
||||
},
|
||||
);
|
||||
isa_targets.push(isa::Target {
|
||||
dst_pc,
|
||||
drop_keep: drop_keep.into(),
|
||||
});
|
||||
}
|
||||
self.ins.push(isa::Instruction::BrTable(
|
||||
isa_targets.into_boxed_slice(),
|
||||
));
|
||||
self.ins
|
||||
.push(isa::Instruction::BrTable(isa_targets.into_boxed_slice()));
|
||||
}
|
||||
|
||||
/// Create a new unresolved label.
|
||||
fn new_label(&mut self) -> LabelId {
|
||||
let label_idx = self.labels.len();
|
||||
self.labels.push(
|
||||
(Label::NotResolved, Vec::new()),
|
||||
);
|
||||
self.labels.push((Label::NotResolved, Vec::new()));
|
||||
LabelId(label_idx)
|
||||
}
|
||||
|
||||
|
@ -1762,14 +1877,18 @@ impl Sink {
|
|||
/// Consume this Sink and returns isa::Instructions.
|
||||
fn into_inner(self) -> isa::Instructions {
|
||||
// At this moment all labels should be resolved.
|
||||
assert!({
|
||||
self.labels.iter().all(|(state, unresolved)|
|
||||
match (state, unresolved) {
|
||||
(Label::Resolved(_), unresolved) if unresolved.is_empty() => true,
|
||||
_ => false,
|
||||
}
|
||||
)
|
||||
}, "there are unresolved labels left: {:?}", self.labels);
|
||||
assert!(
|
||||
{
|
||||
self.labels
|
||||
.iter()
|
||||
.all(|(state, unresolved)| match (state, unresolved) {
|
||||
(Label::Resolved(_), unresolved) if unresolved.is_empty() => true,
|
||||
_ => false,
|
||||
})
|
||||
},
|
||||
"there are unresolved labels left: {:?}",
|
||||
self.labels
|
||||
);
|
||||
self.ins
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::*;
|
||||
use core::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error;
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashmap_core::HashSet;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet;
|
||||
|
||||
use parity_wasm::elements::{
|
||||
BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction,
|
||||
ResizableLimits, TableType, ValueType, InitExpr, Type,
|
||||
};
|
||||
use common::stack;
|
||||
use self::context::ModuleContextBuilder;
|
||||
use self::func::FunctionReader;
|
||||
use memory_units::Pages;
|
||||
use common::stack;
|
||||
use isa;
|
||||
use memory_units::Pages;
|
||||
use parity_wasm::elements::{
|
||||
BlockType, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, MemoryType,
|
||||
Module, ResizableLimits, TableType, Type, ValueType,
|
||||
};
|
||||
|
||||
mod context;
|
||||
mod func;
|
||||
|
@ -158,7 +158,8 @@ pub fn deny_floating_point(module: &Module) -> Result<(), Error> {
|
|||
if let Some(typ) = types.get(sig.type_ref() as usize) {
|
||||
match *typ {
|
||||
Type::Function(ref func) => {
|
||||
if func.params()
|
||||
if func
|
||||
.params()
|
||||
.iter()
|
||||
.chain(func.return_type().as_ref())
|
||||
.any(|&typ| typ == ValueType::F32 || typ == ValueType::F64)
|
||||
|
@ -189,8 +190,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
|||
.map(|&Type::Function(ref ty)| ty)
|
||||
.cloned()
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}).unwrap_or_default(),
|
||||
);
|
||||
|
||||
// Fill elements with imported values.
|
||||
|
@ -245,32 +245,32 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
|||
if function_section_len != code_section_len {
|
||||
return Err(Error(format!(
|
||||
"length of function section is {}, while len of code section is {}",
|
||||
function_section_len,
|
||||
code_section_len
|
||||
function_section_len, code_section_len
|
||||
)));
|
||||
}
|
||||
|
||||
// validate every function body in user modules
|
||||
if function_section_len != 0 {
|
||||
// tests use invalid code
|
||||
let function_section = module.function_section().expect(
|
||||
"function_section_len != 0; qed",
|
||||
);
|
||||
let code_section = module.code_section().expect(
|
||||
"function_section_len != 0; function_section_len == code_section_len; qed",
|
||||
);
|
||||
let function_section = module
|
||||
.function_section()
|
||||
.expect("function_section_len != 0; qed");
|
||||
let code_section = module
|
||||
.code_section()
|
||||
.expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
||||
// check every function body
|
||||
for (index, function) in function_section.entries().iter().enumerate() {
|
||||
let function_body = code_section.bodies().get(index as usize).ok_or(
|
||||
Error(format!(
|
||||
"Missing body for function {}",
|
||||
index
|
||||
)),
|
||||
)?;
|
||||
let code = FunctionReader::read_function(&context, function, function_body)
|
||||
.map_err(|e| {
|
||||
let function_body = code_section
|
||||
.bodies()
|
||||
.get(index as usize)
|
||||
.ok_or(Error(format!("Missing body for function {}", index)))?;
|
||||
let code =
|
||||
FunctionReader::read_function(&context, function, function_body).map_err(|e| {
|
||||
let Error(ref msg) = e;
|
||||
Error(format!("Function #{} reading/validation error: {}", index, msg))
|
||||
Error(format!(
|
||||
"Function #{} reading/validation error: {}",
|
||||
index, msg
|
||||
))
|
||||
})?;
|
||||
code_map.push(code);
|
||||
}
|
||||
|
@ -293,9 +293,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
|||
// HashSet::insert returns false if item already in set.
|
||||
let duplicate = export_names.insert(export.field()) == false;
|
||||
if duplicate {
|
||||
return Err(Error(
|
||||
format!("duplicate export {}", export.field()),
|
||||
));
|
||||
return Err(Error(format!("duplicate export {}", export.field())));
|
||||
}
|
||||
match *export.internal() {
|
||||
Internal::Function(function_index) => {
|
||||
|
@ -382,10 +380,7 @@ pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(ValidatedModule {
|
||||
module,
|
||||
code_map,
|
||||
})
|
||||
Ok(ValidatedModule { module, code_map })
|
||||
}
|
||||
|
||||
fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
||||
|
@ -413,15 +408,15 @@ fn validate_table_type(table_type: &TableType) -> Result<(), Error> {
|
|||
|
||||
fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) -> Result<(), Error> {
|
||||
let init = global_entry.init_expr();
|
||||
let init_expr_ty = expr_const_type(init, globals)?;
|
||||
if init_expr_ty != global_entry.global_type().content_type() {
|
||||
return Err(Error(format!(
|
||||
"Trying to initialize variable of type {:?} with value of type {:?}",
|
||||
global_entry.global_type().content_type(),
|
||||
init_expr_ty
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
let init_expr_ty = expr_const_type(init, globals)?;
|
||||
if init_expr_ty != global_entry.global_type().content_type() {
|
||||
return Err(Error(format!(
|
||||
"Trying to initialize variable of type {:?} with value of type {:?}",
|
||||
global_entry.global_type().content_type(),
|
||||
init_expr_ty
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns type of this constant expression.
|
||||
|
@ -437,21 +432,20 @@ fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result<Value
|
|||
Instruction::I64Const(_) => ValueType::I64,
|
||||
Instruction::F32Const(_) => ValueType::F32,
|
||||
Instruction::F64Const(_) => ValueType::F64,
|
||||
Instruction::GetGlobal(idx) => {
|
||||
match globals.get(idx as usize) {
|
||||
Some(target_global) => {
|
||||
if target_global.is_mutable() {
|
||||
return Err(Error(format!("Global {} is mutable", idx)));
|
||||
}
|
||||
target_global.content_type()
|
||||
}
|
||||
None => {
|
||||
return Err(Error(
|
||||
format!("Global {} doesn't exists or not yet defined", idx),
|
||||
))
|
||||
Instruction::GetGlobal(idx) => match globals.get(idx as usize) {
|
||||
Some(target_global) => {
|
||||
if target_global.is_mutable() {
|
||||
return Err(Error(format!("Global {} is mutable", idx)));
|
||||
}
|
||||
target_global.content_type()
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(Error(format!(
|
||||
"Global {} doesn't exists or not yet defined",
|
||||
idx
|
||||
)))
|
||||
}
|
||||
},
|
||||
_ => return Err(Error("Non constant opcode in init expr".into())),
|
||||
};
|
||||
if code[1] != Instruction::End {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use super::{validate_module, ValidatedModule};
|
||||
use isa;
|
||||
use parity_wasm::builder::module;
|
||||
use parity_wasm::elements::{
|
||||
External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType,
|
||||
Instruction, Instructions, TableType, ValueType, BlockType, deserialize_buffer,
|
||||
Module,
|
||||
deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr,
|
||||
Instruction, Instructions, MemoryType, Module, TableType, ValueType,
|
||||
};
|
||||
use isa;
|
||||
use wabt;
|
||||
|
||||
#[test]
|
||||
|
@ -27,45 +26,34 @@ fn limits() {
|
|||
|
||||
for (min, max, is_valid) in test_cases {
|
||||
// defined table
|
||||
let m = module()
|
||||
.table()
|
||||
.with_min(min)
|
||||
.with_max(max)
|
||||
.build()
|
||||
.build();
|
||||
let m = module().table().with_min(min).with_max(max).build().build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
|
||||
// imported table
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(min, max))
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(min, max)),
|
||||
)).build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
|
||||
// defined memory
|
||||
let m = module()
|
||||
.memory()
|
||||
.with_min(min)
|
||||
.with_max(max)
|
||||
.build()
|
||||
.with_min(min)
|
||||
.with_max(max)
|
||||
.build()
|
||||
.build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
|
||||
// imported table
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(min, max))
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(min, max)),
|
||||
)).build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
}
|
||||
}
|
||||
|
@ -73,92 +61,63 @@ fn limits() {
|
|||
#[test]
|
||||
fn global_init_const() {
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(
|
||||
vec![Instruction::I32Const(42), Instruction::End]
|
||||
)
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// init expr type differs from declared global type
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I64, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I64, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_init_global() {
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false))
|
||||
)
|
||||
)
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||
)).with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// get_global can reference only previously defined globals
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// get_global can reference only const globals
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true))
|
||||
)
|
||||
)
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||
)).with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// get_global in init_expr can only refer to imported globals.
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, false),
|
||||
InitExpr::new(vec![Instruction::I32Const(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, false),
|
||||
InitExpr::new(vec![Instruction::I32Const(0), Instruction::End]),
|
||||
)).with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -166,35 +125,26 @@ fn global_init_global() {
|
|||
fn global_init_misc() {
|
||||
// without delimiting End opcode
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42)])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42)]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// empty init expr
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// not an constant opcode used
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::Unreachable, Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::Unreachable, Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -202,31 +152,25 @@ fn global_init_misc() {
|
|||
fn module_limits_validity() {
|
||||
// module cannot contain more than 1 memory atm.
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(10, None))
|
||||
)
|
||||
)
|
||||
.memory()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.with_import(ImportEntry::new(
|
||||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(10, None)),
|
||||
)).memory()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// module cannot contain more than 1 table atm.
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(10, None))
|
||||
)
|
||||
)
|
||||
.table()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.with_import(ImportEntry::new(
|
||||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(10, None)),
|
||||
)).table()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
@ -236,19 +180,27 @@ fn funcs() {
|
|||
// recursive function calls is legal.
|
||||
let m = module()
|
||||
.function()
|
||||
.signature().return_type().i32().build()
|
||||
.body().with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(1),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.signature()
|
||||
.return_type()
|
||||
.i32()
|
||||
.build()
|
||||
.body()
|
||||
.with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(1),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.function()
|
||||
.signature().return_type().i32().build()
|
||||
.body().with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(0),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.signature()
|
||||
.return_type()
|
||||
.i32()
|
||||
.build()
|
||||
.body()
|
||||
.with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(0),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
}
|
||||
|
@ -257,26 +209,20 @@ fn funcs() {
|
|||
fn globals() {
|
||||
// import immutable global is legal.
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false))
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// import mutable global is invalid.
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true))
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -284,21 +230,23 @@ fn globals() {
|
|||
fn if_else_with_return_type_validation() {
|
||||
let m = module()
|
||||
.function()
|
||||
.signature().build()
|
||||
.body().with_instructions(Instructions::new(vec![
|
||||
Instruction::I32Const(1),
|
||||
Instruction::If(BlockType::NoResult),
|
||||
Instruction::I32Const(1),
|
||||
Instruction::If(BlockType::Value(ValueType::I32)),
|
||||
Instruction::I32Const(1),
|
||||
Instruction::Else,
|
||||
Instruction::I32Const(2),
|
||||
Instruction::End,
|
||||
Instruction::Drop,
|
||||
Instruction::End,
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.signature()
|
||||
.build()
|
||||
.body()
|
||||
.with_instructions(Instructions::new(vec![
|
||||
Instruction::I32Const(1),
|
||||
Instruction::If(BlockType::NoResult),
|
||||
Instruction::I32Const(1),
|
||||
Instruction::If(BlockType::Value(ValueType::I32)),
|
||||
Instruction::I32Const(1),
|
||||
Instruction::Else,
|
||||
Instruction::I32Const(2),
|
||||
Instruction::End,
|
||||
Instruction::Drop,
|
||||
Instruction::End,
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.build();
|
||||
validate_module(m).unwrap();
|
||||
}
|
||||
|
@ -323,7 +271,7 @@ fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
|
|||
instructions.push(instruction.clone());
|
||||
pcs.push(pc);
|
||||
} else {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,32 +280,34 @@ fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
|
|||
|
||||
#[test]
|
||||
fn implicit_return_no_value() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
})
|
||||
]
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
})]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_with_value() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (result i32)
|
||||
i32.const 0
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -372,32 +322,34 @@ fn implicit_return_with_value() {
|
|||
|
||||
#[test]
|
||||
fn implicit_return_param() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::None,
|
||||
}),]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_local() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -412,14 +364,16 @@ fn get_local() {
|
|||
|
||||
#[test]
|
||||
fn explicit_return() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
return
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -438,7 +392,8 @@ fn explicit_return() {
|
|||
|
||||
#[test]
|
||||
fn add_params() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (param i32) (result i32)
|
||||
get_local 0
|
||||
|
@ -446,7 +401,8 @@ fn add_params() {
|
|||
i32.add
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -468,7 +424,8 @@ fn add_params() {
|
|||
|
||||
#[test]
|
||||
fn drop_locals() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
(local i32)
|
||||
|
@ -476,7 +433,8 @@ fn drop_locals() {
|
|||
set_local 1
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -492,7 +450,8 @@ fn drop_locals() {
|
|||
|
||||
#[test]
|
||||
fn if_without_else() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let (code, pcs) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
i32.const 1
|
||||
|
@ -503,7 +462,8 @@ fn if_without_else() {
|
|||
i32.const 3
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -517,7 +477,7 @@ fn if_without_else() {
|
|||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1, // 1 param
|
||||
drop: 1, // 1 param
|
||||
keep: isa::Keep::Single, // 1 result
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
|
@ -531,7 +491,8 @@ fn if_without_else() {
|
|||
|
||||
#[test]
|
||||
fn if_else() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let (code, pcs) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
(local i32)
|
||||
|
@ -545,7 +506,8 @@ fn if_else() {
|
|||
end
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -578,7 +540,8 @@ fn if_else() {
|
|||
|
||||
#[test]
|
||||
fn if_else_returns_result() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let (code, pcs) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -590,7 +553,8 @@ fn if_else_returns_result() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -622,7 +586,8 @@ fn if_else_returns_result() {
|
|||
|
||||
#[test]
|
||||
fn if_else_branch_from_true_branch() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let (code, pcs) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -638,7 +603,8 @@ fn if_else_branch_from_true_branch() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -680,7 +646,8 @@ fn if_else_branch_from_true_branch() {
|
|||
|
||||
#[test]
|
||||
fn if_else_branch_from_false_branch() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let (code, pcs) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -696,7 +663,8 @@ fn if_else_branch_from_false_branch() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -738,7 +706,8 @@ fn if_else_branch_from_false_branch() {
|
|||
|
||||
#[test]
|
||||
fn loop_() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop (result i32)
|
||||
|
@ -749,7 +718,8 @@ fn loop_() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -773,28 +743,29 @@ fn loop_() {
|
|||
|
||||
#[test]
|
||||
fn loop_empty() {
|
||||
let (code, _) = compile(r#"
|
||||
let (code, _) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop
|
||||
end
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brtable() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let (code, pcs) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1
|
||||
|
@ -805,7 +776,8 @@ fn brtable() {
|
|||
end
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -838,7 +810,8 @@ fn brtable() {
|
|||
|
||||
#[test]
|
||||
fn brtable_returns_result() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let (code, pcs) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1 (result i32)
|
||||
|
@ -852,7 +825,8 @@ fn brtable_returns_result() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -888,7 +862,8 @@ fn brtable_returns_result() {
|
|||
|
||||
#[test]
|
||||
fn wabt_example() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let (code, pcs) = compile(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
block $exit
|
||||
|
@ -901,7 +876,8 @@ fn wabt_example() {
|
|||
return
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
|
|
@ -22,9 +22,7 @@ impl<'a> Locals<'a> {
|
|||
for locals_group in local_groups {
|
||||
acc = acc
|
||||
.checked_add(locals_group.count())
|
||||
.ok_or_else(||
|
||||
Error(String::from("Locals range not in 32-bit range"))
|
||||
)?;
|
||||
.ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
|
||||
}
|
||||
|
||||
Ok(Locals {
|
||||
|
@ -125,9 +123,6 @@ mod tests {
|
|||
Local::new(u32::max_value(), ValueType::I32),
|
||||
Local::new(1, ValueType::I64),
|
||||
];
|
||||
assert_matches!(
|
||||
Locals::new(&[], &local_groups),
|
||||
Err(_)
|
||||
);
|
||||
assert_matches!(Locals::new(&[], &local_groups), Err(_));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
mod run;
|
||||
|
||||
macro_rules! run_test {
|
||||
($label: expr, $test_name: ident) => (
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
self::run::spec($label)
|
||||
}
|
||||
);
|
||||
($label: expr, $test_name: ident) => {
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
self::run::spec($label)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
run_test!("address", wasm_address);
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
|
||||
use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value};
|
||||
use wasmi::memory_units::Pages;
|
||||
use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor,
|
||||
GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor,
|
||||
MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap};
|
||||
use wasmi::{
|
||||
Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance,
|
||||
GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module,
|
||||
ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature,
|
||||
TableDescriptor, TableInstance, TableRef, Trap,
|
||||
};
|
||||
|
||||
fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
|
||||
match val {
|
||||
|
@ -21,30 +23,30 @@ fn spec_to_runtime_value(val: Value<u32, u64>) -> RuntimeValue {
|
|||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Load(String),
|
||||
Start(Trap),
|
||||
Script(script::Error),
|
||||
Interpreter(InterpreterError),
|
||||
Load(String),
|
||||
Start(Trap),
|
||||
Script(script::Error),
|
||||
Interpreter(InterpreterError),
|
||||
}
|
||||
|
||||
impl From<InterpreterError> for Error {
|
||||
fn from(e: InterpreterError) -> Error {
|
||||
Error::Interpreter(e)
|
||||
}
|
||||
fn from(e: InterpreterError) -> Error {
|
||||
Error::Interpreter(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<script::Error> for Error {
|
||||
fn from(e: script::Error) -> Error {
|
||||
Error::Script(e)
|
||||
}
|
||||
fn from(e: script::Error) -> Error {
|
||||
Error::Script(e)
|
||||
}
|
||||
}
|
||||
|
||||
struct SpecModule {
|
||||
table: TableRef,
|
||||
memory: MemoryRef,
|
||||
global_i32: GlobalRef,
|
||||
global_f32: GlobalRef,
|
||||
global_f64: GlobalRef,
|
||||
table: TableRef,
|
||||
memory: MemoryRef,
|
||||
global_i32: GlobalRef,
|
||||
global_f32: GlobalRef,
|
||||
global_f64: GlobalRef,
|
||||
}
|
||||
|
||||
impl SpecModule {
|
||||
|
@ -62,27 +64,27 @@ impl SpecModule {
|
|||
const PRINT_FUNC_INDEX: usize = 0;
|
||||
|
||||
impl Externals for SpecModule {
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
match index {
|
||||
PRINT_FUNC_INDEX => {
|
||||
println!("print: {:?}", args);
|
||||
Ok(None)
|
||||
}
|
||||
_ => panic!("SpecModule doesn't provide function at index {}", index),
|
||||
}
|
||||
}
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
match index {
|
||||
PRINT_FUNC_INDEX => {
|
||||
println!("print: {:?}", args);
|
||||
Ok(None)
|
||||
}
|
||||
_ => panic!("SpecModule doesn't provide function at index {}", index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleImportResolver for SpecModule {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
func_type: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
fn resolve_func(
|
||||
&self,
|
||||
field_name: &str,
|
||||
func_type: &Signature,
|
||||
) -> Result<FuncRef, InterpreterError> {
|
||||
let index = match field_name {
|
||||
"print" => PRINT_FUNC_INDEX,
|
||||
"print_i32" => PRINT_FUNC_INDEX,
|
||||
|
@ -104,15 +106,15 @@ impl ModuleImportResolver for SpecModule {
|
|||
));
|
||||
}
|
||||
|
||||
let func = FuncInstance::alloc_host(func_type.clone(), index);
|
||||
let func = FuncInstance::alloc_host(func_type.clone(), index);
|
||||
return Ok(func);
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_global(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, InterpreterError> {
|
||||
fn resolve_global(
|
||||
&self,
|
||||
field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, InterpreterError> {
|
||||
match field_name {
|
||||
"global_i32" => Ok(self.global_i32.clone()),
|
||||
"global_f32" => Ok(self.global_f32.clone()),
|
||||
|
@ -190,7 +192,8 @@ impl SpecDriver {
|
|||
fn module_or_last(&self, name: Option<&str>) -> Result<ModuleRef, InterpreterError> {
|
||||
match name {
|
||||
Some(name) => self.module(name),
|
||||
None => self.last_module
|
||||
None => self
|
||||
.last_module
|
||||
.clone()
|
||||
.ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())),
|
||||
}
|
||||
|
@ -301,7 +304,8 @@ fn run_action(
|
|||
"Expected program to have loaded module {:?}",
|
||||
module
|
||||
));
|
||||
let vec_args = args.iter()
|
||||
let vec_args = args
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(spec_to_runtime_value)
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -322,8 +326,7 @@ fn run_action(
|
|||
.export_by_name(&field)
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected to have export with name {}", field))
|
||||
})?
|
||||
.as_global()
|
||||
})?.as_global()
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
InterpreterError::Global(format!("Expected export {} to be a global", field))
|
||||
|
@ -345,9 +348,12 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
|||
use std::io::Read;
|
||||
let mut spec_source = Vec::new();
|
||||
let mut spec_file = File::open(&spec_script_path).expect("Can't open file");
|
||||
spec_file.read_to_end(&mut spec_source).expect("Can't read file");
|
||||
spec_file
|
||||
.read_to_end(&mut spec_source)
|
||||
.expect("Can't read file");
|
||||
|
||||
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)).expect("Can't read spec script");
|
||||
let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name))
|
||||
.expect("Can't read spec script");
|
||||
let mut errors = vec![];
|
||||
|
||||
while let Some(Command { kind, line }) = parser.next()? {
|
||||
|
@ -435,7 +441,7 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
|||
let result = run_action(&mut spec_driver, &action);
|
||||
match result {
|
||||
Ok(result) => panic!("Expected exhaustion, got result: {:?}", result),
|
||||
Err(_e) => {},
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertTrap { action, .. } => {
|
||||
|
@ -456,13 +462,13 @@ fn try_spec(name: &str) -> Result<(), Error> {
|
|||
let module_load = try_load(&module.into_vec(), &mut spec_driver);
|
||||
match module_load {
|
||||
Ok(_) => panic!("Expected invalid module definition, got some module!"),
|
||||
Err(_e) => {},
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::AssertUninstantiable { module, .. } => {
|
||||
match try_load(&module.into_vec(), &mut spec_driver) {
|
||||
Ok(_) => panic!("Expected error running start function at line {}", line),
|
||||
Err(_e) => {},
|
||||
Err(_e) => {}
|
||||
}
|
||||
}
|
||||
CommandKind::Register { name, as_name, .. } => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Official spec testsuite.
|
||||
|
||||
extern crate wasmi;
|
||||
extern crate wabt;
|
||||
extern crate wasmi;
|
||||
|
||||
mod spec;
|
||||
|
|
Loading…
Reference in New Issue