drop interpreter

This commit is contained in:
NikVolf
2018-02-21 13:37:13 +03:00
parent d81dd49f72
commit 4e28d0c61e
30 changed files with 0 additions and 10602 deletions

View File

@ -2,7 +2,6 @@ dist: trusty
sudo: required sudo: required
language: language:
- rust - rust
- cpp
addons: addons:
apt: apt:
sources: sources:
@ -17,7 +16,6 @@ script:
- cargo build --release --verbose - cargo build --release --verbose
- cargo test --release --verbose - cargo test --release --verbose
- cargo test --release --manifest-path=spec/Cargo.toml - cargo test --release --manifest-path=spec/Cargo.toml
- cargo test --manifest-path=pwasm-emscripten/Cargo.toml
- cargo run --example bench-decoder --release - cargo run --example bench-decoder --release
after_success: |- after_success: |-
[ $TRAVIS_BRANCH = master ] && [ $TRAVIS_BRANCH = master ] &&

View File

@ -1,37 +0,0 @@
// In this example we execute a contract funciton exported as "_call"
#![allow(deprecated)]
extern crate parity_wasm;
use std::env::args;
use parity_wasm::ModuleInstanceInterface;
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;
}
// Intrepreter initialization.
let program = parity_wasm::ProgramInstance::new();
// Here we load module using dedicated for this purpose
// `deserialize_file` function (which works only with modules)
let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load 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 module = program.add_module("main", module, None).expect("Failed to initialize module");
// 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: {:?}", module.execute_export("_call", vec![parity_wasm::RuntimeValue::I32(argument)].into()));
}

View File

@ -1,85 +0,0 @@
#![allow(deprecated)]
extern crate parity_wasm;
use std::env::args;
use parity_wasm::{interpreter, ModuleInstanceInterface, RuntimeValue};
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType};
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);
// Intrepreter initialization.
let program = parity_wasm::ProgramInstance::new();
let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized");
// Extracts call arguments from command-line arguments
let execution_params = {
// 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));
// 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,
};
// 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;
// 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
let args: Vec<RuntimeValue> = function_type.params().iter().enumerate().map(|(i, value)| match value {
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i]))),
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i]))),
}).collect();
interpreter::ExecutionParams::from(args)
};
// 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 module = program.add_module("main", module, None).expect("Failed to initialize module");
println!("Result: {:?}", module.execute_export(func_name, execution_params).expect(""));
}

View File

@ -1 +0,0 @@
target

View File

@ -1,7 +0,0 @@
[package]
name = "pwasm-emscripten"
version = "0.1.0"
authors = ["Sergey Pepyakin <s.pepyakin@gmail.com>"]
[dependencies]
parity-wasm = "0.18"

View File

@ -1,263 +0,0 @@
//! This crate provides some of the simplest exports
//! from the Emscripten runtime, such as `STACKTOP` or `abort`.
extern crate parity_wasm;
use std::sync::{Arc, Weak};
use parity_wasm::builder::module;
use parity_wasm::elements::{ExportEntry, Internal, ValueType};
use parity_wasm::interpreter::Error;
use parity_wasm::interpreter::{native_module, UserDefinedElements, UserFunctionDescriptor, UserFunctionExecutor};
use parity_wasm::interpreter::{CallerContext, ModuleInstance, ModuleInstanceInterface};
use parity_wasm::interpreter::RuntimeValue;
use parity_wasm::interpreter::ProgramInstance;
use parity_wasm::interpreter::{VariableInstance, VariableType};
/// Memory address, at which stack begins.
const DEFAULT_STACK_TOP: u32 = 256 * 1024;
/// Memory, allocated for stack.
const DEFAULT_TOTAL_STACK: u32 = 5 * 1024 * 1024;
/// Total memory, allocated by default.
const DEFAULT_TOTAL_MEMORY: u32 = 16 * 1024 * 1024;
/// Whether memory can be enlarged, or not.
const DEFAULT_ALLOW_MEMORY_GROWTH: bool = true;
/// Default tableBase variable value.
const DEFAULT_TABLE_BASE: u32 = 0;
/// Default tableBase variable value.
const DEFAULT_MEMORY_BASE: u32 = 0;
/// Default table size.
const DEFAULT_TABLE_SIZE: u32 = 64;
/// Index of default memory.
const INDEX_MEMORY: u32 = 0;
/// Index of default table.
const INDEX_TABLE: u32 = 0;
const LINEAR_MEMORY_PAGE_SIZE: u32 = 64 * 1024;
/// Emscripten environment parameters.
pub struct EmscriptenParams {
/// Stack size in bytes.
pub total_stack: u32,
/// Total memory size in bytes.
pub total_memory: u32,
/// Allow memory growth.
pub allow_memory_growth: bool,
/// Table size.
pub table_size: u32,
/// Static reserve, if any
pub static_size: Option<u32>,
}
struct EmscriptenFunctionExecutor {
total_mem_global: Arc<VariableInstance>,
}
impl<'a> UserFunctionExecutor for EmscriptenFunctionExecutor {
fn execute(
&mut self,
name: &str,
context: CallerContext,
) -> Result<Option<RuntimeValue>, Error> {
match name {
"_abort" | "abort" => {
Err(Error::Trap("abort".into()).into())
},
"assert" => {
let condition = context.value_stack.pop_as::<i32>()?;
if condition == 0 {
Err(Error::Trap("assertion failed".into()))
} else {
Ok(None)
}
},
"enlargeMemory" => {
// TODO: support memory enlarge
Ok(Some(RuntimeValue::I32(0)))
},
"getTotalMemory" => {
let total_memory = self.total_mem_global.get();
Ok(Some(total_memory))
},
_ => Err(Error::Trap("not implemented".into()).into()),
}
}
}
pub fn env_module(params: EmscriptenParams) -> Result<Arc<ModuleInstanceInterface>, Error> {
debug_assert!(params.total_stack < params.total_memory);
debug_assert!((params.total_stack % LINEAR_MEMORY_PAGE_SIZE) == 0);
debug_assert!((params.total_memory % LINEAR_MEMORY_PAGE_SIZE) == 0);
let stack_top = params.static_size.unwrap_or(DEFAULT_STACK_TOP);
// Build module with defined memory and table,
// instantiate it and wrap into an Arc.
let instance = {
let builder = module()
// memory regions
.memory()
.with_min(params.total_memory / LINEAR_MEMORY_PAGE_SIZE)
.with_max(params.max_memory().map(|m| m / LINEAR_MEMORY_PAGE_SIZE))
.build()
.with_export(ExportEntry::new("memory".into(), Internal::Memory(INDEX_MEMORY)))
// tables
.table()
.with_min(params.table_size)
.build()
.with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE)));
let mut instance = ModuleInstance::new(Weak::default(), "env".into(), builder.build())?;
instance.instantiate(None)?;
Arc::new(instance)
};
let total_mem_global = Arc::new(
VariableInstance::new(
false,
VariableType::I32,
RuntimeValue::I32(params.total_memory as i32),
).unwrap(),
);
let function_executor = EmscriptenFunctionExecutor {
total_mem_global: Arc::clone(&total_mem_global),
};
const SIGNATURES: &'static [UserFunctionDescriptor] = &[
UserFunctionDescriptor::Static("_abort", &[], None),
UserFunctionDescriptor::Static("abort", &[], None),
UserFunctionDescriptor::Static("assert", &[ValueType::I32], None),
UserFunctionDescriptor::Static("enlargeMemory", &[], Some(ValueType::I32)),
UserFunctionDescriptor::Static("getTotalMemory", &[], Some(ValueType::I32)),
];
let elements = UserDefinedElements {
executor: Some(function_executor),
globals: vec![
(
"STACK_BASE".into(),
Arc::new(
VariableInstance::new(
false,
VariableType::I32,
RuntimeValue::I32(stack_top as i32),
).unwrap(),
),
),
(
"STACKTOP".into(),
Arc::new(
VariableInstance::new(
false,
VariableType::I32,
RuntimeValue::I32(stack_top as i32),
).unwrap(),
),
),
(
"STACK_MAX".into(),
Arc::new(
VariableInstance::new(
false,
VariableType::I32,
RuntimeValue::I32((stack_top + params.total_stack) as i32),
).unwrap(),
),
),
(
"DYNAMIC_BASE".into(),
Arc::new(
VariableInstance::new(
false,
VariableType::I32,
RuntimeValue::I32((stack_top + params.total_stack) as i32),
).unwrap(),
),
),
(
"DYNAMICTOP_PTR".into(),
Arc::new(
VariableInstance::new(
false,
VariableType::I32,
RuntimeValue::I32((stack_top + params.total_stack) as i32),
).unwrap(),
),
),
(
"EXITSTATUS".into(),
Arc::new(
VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(0)).unwrap(),
),
),
(
"tableBase".into(),
Arc::new(
VariableInstance::new(
false,
VariableType::I32,
RuntimeValue::I32(DEFAULT_TABLE_BASE as i32),
).unwrap(),
),
),
(
"memoryBase".into(),
Arc::new(
VariableInstance::new(
false,
VariableType::I32,
RuntimeValue::I32(DEFAULT_MEMORY_BASE as i32),
).unwrap(),
),
),
("TOTAL_MEMORY".into(), total_mem_global),
].into_iter()
.collect(),
functions: ::std::borrow::Cow::from(SIGNATURES),
};
Ok(native_module(instance, elements)?)
}
impl Default for EmscriptenParams {
fn default() -> Self {
EmscriptenParams {
total_stack: DEFAULT_TOTAL_STACK,
total_memory: DEFAULT_TOTAL_MEMORY,
allow_memory_growth: DEFAULT_ALLOW_MEMORY_GROWTH,
table_size: DEFAULT_TABLE_SIZE,
static_size: None,
}
}
}
impl EmscriptenParams {
fn max_memory(&self) -> Option<u32> {
if self.allow_memory_growth { None } else { Some(self.total_memory) }
}
}
pub fn program_with_emscripten_env(params: EmscriptenParams) -> Result<ProgramInstance, Error> {
let program = ProgramInstance::new();
program.insert_loaded_module("env", env_module(params)?)?;
Ok(program)
}
#[cfg(test)]
mod tests {
use super::program_with_emscripten_env;
use parity_wasm::builder::module;
use parity_wasm::elements::{ImportEntry, External, GlobalType, ValueType};
#[test]
fn import_env_mutable_global() {
let program = program_with_emscripten_env(Default::default()).unwrap();
let module = module()
.with_import(ImportEntry::new("env".into(), "STACKTOP".into(), External::Global(GlobalType::new(ValueType::I32, false))))
.build();
program.add_module("main", module, None).unwrap();
}
}

View File

@ -1,40 +0,0 @@
use elements::BlockType;
pub mod stack;
/// Index of default linear memory.
pub const DEFAULT_MEMORY_INDEX: u32 = 0;
/// Index of default table.
pub const DEFAULT_TABLE_INDEX: u32 = 0;
/// Control stack frame.
#[derive(Debug, Clone)]
pub struct BlockFrame {
/// Frame type.
pub frame_type: BlockFrameType,
/// A signature, which is a block signature type indicating the number and types of result values of the region.
pub block_type: BlockType,
/// A label for reference to block instruction.
pub begin_position: usize,
/// A label for reference from branch instructions.
pub branch_position: usize,
/// A label for reference from end instructions.
pub end_position: usize,
/// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label.
pub value_stack_len: usize,
}
/// Type of block frame.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BlockFrameType {
/// Function frame.
Function,
/// Usual block frame.
Block,
/// Loop frame (branching to the beginning of block).
Loop,
/// True-subblock of if expression.
IfTrue,
/// False-subblock of if expression.
IfFalse,
}

View File

@ -1,113 +0,0 @@
use std::collections::VecDeque;
use std::error;
use std::fmt;
#[derive(Debug)]
pub struct Error(String);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl error::Error for Error {
fn description(&self) -> &str {
&self.0
}
}
/// Stack with limit.
#[derive(Debug)]
pub struct StackWithLimit<T> where T: Clone {
/// Stack values.
values: VecDeque<T>,
/// Stack limit (maximal stack len).
limit: usize,
}
impl<T> StackWithLimit<T> where T: Clone {
pub fn with_data(data: Vec<T>, limit: usize) -> Self {
StackWithLimit {
values: data.into_iter().collect(),
limit: limit
}
}
pub fn with_limit(limit: usize) -> Self {
StackWithLimit {
values: VecDeque::new(),
limit: limit
}
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn limit(&self) -> usize {
self.limit
}
pub fn values(&self) -> &VecDeque<T> {
&self.values
}
pub fn top(&self) -> Result<&T, Error> {
self.values
.back()
.ok_or(Error("non-empty stack expected".into()))
}
pub fn top_mut(&mut self) -> Result<&mut T, Error> {
self.values
.back_mut()
.ok_or(Error("non-empty stack expected".into()))
}
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())));
}
Ok(self.values.get(self.values.len() - 1 - index).expect("checked couple of lines above"))
}
pub fn push(&mut self, value: T) -> Result<(), Error> {
if self.values.len() >= self.limit {
return Err(Error(format!("exceeded stack limit {}", self.limit)));
}
self.values.push_back(value);
Ok(())
}
pub fn push_penultimate(&mut self, value: T) -> Result<(), Error> {
if self.values.is_empty() {
return Err(Error("trying to insert penultimate element into empty stack".into()));
}
self.push(value)?;
let last_index = self.values.len() - 1;
let penultimate_index = last_index - 1;
self.values.swap(last_index, penultimate_index);
Ok(())
}
pub fn pop(&mut self) -> Result<T, Error> {
self.values
.pop_back()
.ok_or(Error("non-empty stack expected".into()))
}
pub fn resize(&mut self, new_size: usize, dummy: T) {
debug_assert!(new_size <= self.values.len());
self.values.resize(new_size, dummy);
}
}

View File

@ -1,166 +0,0 @@
use std::sync::{Arc, Weak};
use std::collections::HashMap;
use elements::{ImportSection, ImportEntry, External, Internal};
use interpreter::Error;
use interpreter::memory::MemoryInstance;
use interpreter::module::{ModuleInstanceInterface, ItemIndex, ExportEntryType, FunctionSignature};
use interpreter::program::ProgramInstanceEssence;
use interpreter::table::TableInstance;
use interpreter::variable::{VariableInstance, VariableType};
/// Module imports.
pub struct ModuleImports {
/// Program instance.
program: Weak<ProgramInstanceEssence>,
/// External functions.
functions: Vec<usize>,
/// External tables.
tables: Vec<usize>,
/// External memory regions.
memory: Vec<usize>,
/// External globals.
globals: Vec<usize>,
}
impl ModuleImports {
/// Create new imports for given import section.
pub fn new(program: Weak<ProgramInstanceEssence>, import_section: Option<&ImportSection>) -> Self {
let mut functions = Vec::new();
let mut tables = Vec::new();
let mut memory = Vec::new();
let mut globals = Vec::new();
if let Some(import_section) = import_section {
for (import_index, import_entry) in import_section.entries().iter().enumerate() {
match import_entry.external() {
&External::Function(_) => functions.push(import_index),
&External::Table(_) => tables.push(import_index),
&External::Memory(_) => memory.push(import_index),
&External::Global(_) => globals.push(import_index),
}
}
}
ModuleImports {
program: program,
functions: functions,
tables: tables,
memory: memory,
globals: globals,
}
}
/// Number of imported tables.
pub fn tables_len(&self) -> usize {
self.tables.len()
}
/// Number of imported memory regions.
pub fn memory_regions_len(&self) -> usize {
self.memory.len()
}
/// Parse function index.
pub fn parse_function_index(&self, index: ItemIndex) -> ItemIndex {
match index {
ItemIndex::IndexSpace(index) => match index.checked_sub(self.functions.len() as u32) {
Some(index) => ItemIndex::Internal(index),
None => ItemIndex::External(self.functions[index as usize] as u32),
},
index @ _ => index,
}
}
/// Parse table index.
pub fn parse_table_index(&self, index: ItemIndex) -> ItemIndex {
match index {
ItemIndex::IndexSpace(index) => match index.checked_sub(self.tables.len() as u32) {
Some(index) => ItemIndex::Internal(index),
None => ItemIndex::External(self.tables[index as usize] as u32),
},
index @ _ => index,
}
}
/// Parse memory index.
pub fn parse_memory_index(&self, index: ItemIndex) -> ItemIndex {
match index {
ItemIndex::IndexSpace(index) => match index.checked_sub(self.memory.len() as u32) {
Some(index) => ItemIndex::Internal(index),
None => ItemIndex::External(self.memory[index as usize] as u32),
},
index @ _ => index,
}
}
/// Parse global index.
pub fn parse_global_index(&self, index: ItemIndex) -> ItemIndex {
match index {
ItemIndex::IndexSpace(index) => match index.checked_sub(self.globals.len() as u32) {
Some(index) => ItemIndex::Internal(index),
None => ItemIndex::External(self.globals[index as usize] as u32),
},
index @ _ => index,
}
}
/// Get module reference.
pub fn module<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, name: &str) -> Result<Arc<ModuleInstanceInterface + 'a>, Error> {
if let Some(externals) = externals {
if let Some(module) = externals.get(name).cloned() {
return Ok(module);
}
}
self.program
.upgrade()
.ok_or(Error::Program("program unloaded".into()))
.and_then(|p| p.module(name).ok_or(Error::Program(format!("module {} is not loaded", name))))
}
/// Get function index.
pub fn function<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry, required_type: Option<FunctionSignature>) -> Result<u32, Error> {
let (_, export) = self.external_export(externals, import, &required_type.map(|ft| ExportEntryType::Function(ft)).unwrap_or(ExportEntryType::Any))?;
if let Internal::Function(external_index) = export {
return Ok(external_index);
}
Err(Error::Program(format!("wrong import {} from module {} (expecting function)", import.field(), import.module())))
}
/// Get table reference.
pub fn table<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<Arc<TableInstance>, Error> {
let (module, export) = self.external_export(externals, import, &ExportEntryType::Any)?;
if let Internal::Table(external_index) = export {
return module.table(ItemIndex::Internal(external_index));
}
Err(Error::Program(format!("wrong import {} from module {} (expecting table)", import.field(), import.module())))
}
/// Get memory reference.
pub fn memory<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<Arc<MemoryInstance>, Error> {
let (module, export) = self.external_export(externals, import, &ExportEntryType::Any)?;
if let Internal::Memory(external_index) = export {
return module.memory(ItemIndex::Internal(external_index));
}
Err(Error::Program(format!("wrong import {} from module {} (expecting memory)", import.field(), import.module())))
}
/// Get global reference.
pub fn global<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry, required_type: Option<VariableType>) -> Result<Arc<VariableInstance>, Error> {
let (module, export) = self.external_export(externals, import, &required_type.clone().map(|rt| ExportEntryType::Global(rt)).unwrap_or(ExportEntryType::Any))?;
if let Internal::Global(external_index) = export {
return module.global(ItemIndex::Internal(external_index), required_type, externals);
}
Err(Error::Program(format!("wrong import {} from module {} (expecting global)", import.field(), import.module())))
}
fn external_export<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry, required_type: &ExportEntryType) -> Result<(Arc<ModuleInstanceInterface + 'a>, Internal), Error> {
self.module(externals, import.module())
.and_then(|m|
m.export_entry(import.field(), required_type)
.map(|e| (m, e)))
}
}

View File

@ -1,280 +0,0 @@
use std::u32;
use std::sync::Arc;
use std::ops::Range;
use std::cmp;
use parking_lot::RwLock;
use elements::{MemoryType, ResizableLimits};
use interpreter::Error;
use interpreter::module::check_limits;
/// Linear memory page size.
pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536;
/// Maximal number of pages.
const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;
/// Linear memory instance.
pub struct MemoryInstance {
/// Memofy limits.
limits: ResizableLimits,
/// Linear memory buffer.
buffer: RwLock<Vec<u8>>,
/// Maximum buffer size.
maximum_size: u32,
}
struct CheckedRegion<'a, B: 'a> where B: ::std::ops::Deref<Target=Vec<u8>> {
buffer: &'a B,
offset: usize,
size: usize,
}
impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref<Target=Vec<u8>> {
fn range(&self) -> Range<usize> {
self.offset..self.offset+self.size
}
fn slice(&self) -> &[u8] {
&self.buffer[self.range()]
}
fn intersects(&self, other: &Self) -> bool {
let low = cmp::max(self.offset, other.offset);
let high = cmp::min(self.offset + self.size, other.offset + other.size);
low < high
}
}
impl MemoryInstance {
/// Create new linear memory instance.
pub fn new(memory_type: &MemoryType) -> Result<Arc<Self>, Error> {
check_limits(memory_type.limits())?;
let maximum_size = match memory_type.limits().maximum() {
Some(maximum_pages) if maximum_pages > LINEAR_MEMORY_MAX_PAGES =>
return Err(Error::Memory(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES))),
Some(maximum_pages) => maximum_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE),
None => u32::MAX,
};
let initial_size = calculate_memory_size(0, memory_type.limits().initial(), maximum_size)
.ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?;
let memory = MemoryInstance {
limits: memory_type.limits().clone(),
buffer: RwLock::new(vec![0; initial_size as usize]),
maximum_size: maximum_size,
};
Ok(Arc::new(memory))
}
/// Return linear memory limits.
pub fn limits(&self) -> &ResizableLimits {
&self.limits
}
/// Return linear memory size (in pages).
pub fn size(&self) -> u32 {
self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
}
/// Get data at given offset.
pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> {
let buffer = self.buffer.read();
let region = self.checked_region(&buffer, offset as usize, size)?;
Ok(region.slice().to_vec())
}
/// Write memory slice into another slice
pub fn get_into(&self, offset: u32, target: &mut [u8]) -> Result<(), Error> {
let buffer = self.buffer.read();
let region = self.checked_region(&buffer, offset as usize, target.len())?;
target.copy_from_slice(region.slice());
Ok(())
}
/// Set data at given offset.
pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> {
let mut buffer = self.buffer.write();
let range = self.checked_region(&buffer, offset as usize, value.len())?.range();
buffer[range].copy_from_slice(value);
Ok(())
}
/// Increases the size of the linear memory by given number of pages.
/// Returns -1 if allocation fails or previous memory size, if succeeds.
pub fn grow(&self, pages: u32) -> Result<u32, Error> {
let mut buffer = self.buffer.write();
let old_size = buffer.len() as u32;
match calculate_memory_size(old_size, pages, self.maximum_size) {
None => Ok(u32::MAX),
Some(new_size) => {
buffer.resize(new_size as usize, 0);
Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
},
}
}
fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error>
where B: ::std::ops::Deref<Target=Vec<u8>>
{
let end = offset.checked_add(size)
.ok_or(Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;
if end > buffer.len() {
return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len())));
}
Ok(CheckedRegion {
buffer: buffer,
offset: offset,
size: size,
})
}
/// Copy memory region. Semantically equivalent to `memmove`.
pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
let buffer = self.buffer.write();
let read_region = self.checked_region(&buffer, src_offset, len)?;
let write_region = self.checked_region(&buffer, dst_offset, len)?;
unsafe { ::std::ptr::copy(
buffer[read_region.range()].as_ptr(),
buffer[write_region.range()].as_ptr() as *mut _,
len,
)}
Ok(())
}
/// Copy memory region, non-overlapping version. Semantically equivalent to `memcpy`,
/// but returns Error if source overlaping with destination.
pub fn copy_nonoverlapping(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
let buffer = self.buffer.write();
let read_region = self.checked_region(&buffer, src_offset, len)?;
let write_region = self.checked_region(&buffer, dst_offset, len)?;
if read_region.intersects(&write_region) {
return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions")))
}
unsafe { ::std::ptr::copy_nonoverlapping(
buffer[read_region.range()].as_ptr(),
buffer[write_region.range()].as_ptr() as *mut _,
len,
)}
Ok(())
}
/// Clear memory region with a specified value. Semantically equivalent to `memset`.
pub fn clear(&self, offset: usize, new_val: u8, len: usize) -> Result<(), Error> {
let mut buffer = self.buffer.write();
let range = self.checked_region(&buffer, offset, len)?.range();
for val in &mut buffer[range] { *val = new_val }
Ok(())
}
/// Zero memory region
pub fn zero(&self, offset: usize, len: usize) -> Result<(), Error> {
self.clear(offset, 0, len)
}
}
fn calculate_memory_size(old_size: u32, additional_pages: u32, maximum_size: u32) -> Option<u32> {
additional_pages
.checked_mul(LINEAR_MEMORY_PAGE_SIZE)
.and_then(|size| size.checked_add(old_size))
.and_then(|size| if size > maximum_size {
None
} else {
Some(size)
})
}
#[cfg(test)]
mod tests {
use super::MemoryInstance;
use interpreter::Error;
use elements::MemoryType;
use std::sync::Arc;
fn create_memory(initial_content: &[u8]) -> Arc<MemoryInstance> {
let mem = MemoryInstance::new(&MemoryType::new(1, Some(1)))
.expect("MemoryInstance created successfuly");
mem.set(0, initial_content).expect("Successful initialize the memory");
mem
}
#[test]
fn copy_overlaps_1() {
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
mem.copy(0, 4, 6).expect("Successfully copy the elements");
let result = mem.get(0, 10).expect("Successfully retrieve the result");
assert_eq!(result, &[0, 1, 2, 3, 0, 1, 2, 3, 4, 5]);
}
#[test]
fn copy_overlaps_2() {
let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
mem.copy(4, 0, 6).expect("Successfully copy the elements");
let result = mem.get(0, 10).expect("Successfully retrieve the result");
assert_eq!(result, &[4, 5, 6, 7, 8, 9, 6, 7, 8, 9]);
}
#[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");
let result = mem.get(10, 10).expect("Successfully retrieve the result");
assert_eq!(result, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
#[test]
fn copy_nonoverlapping_overlaps_1() {
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(_)) => {},
_ => panic!("Expected Error::Memory(_) result, but got {:?}", result),
}
}
#[test]
fn copy_nonoverlapping_overlaps_2() {
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(_)) => {},
_ => panic!("Expected Error::Memory(_), but got {:?}", result),
}
}
#[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");
let result = mem.get(0, 10).expect("To successfully retrieve the result");
assert_eq!(result, &[0x4A; 10]);
}
#[test]
fn get_into() {
let mem = MemoryInstance::new(&MemoryType::new(1, None)).expect("memory instance creation 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");
assert_eq!(data, [17, 129]);
}
}

View File

@ -1,181 +0,0 @@
//! WebAssembly interpreter module.
#![deprecated(since = "0.23", note = "Use wasmi crate to interpret wasm")]
use std::any::TypeId;
use std::error;
use std::fmt;
use validation;
use common;
/// Custom user error.
pub trait UserError: 'static + ::std::fmt::Display + ::std::fmt::Debug {
#[doc(hidden)]
fn __private_get_type_id__(&self) -> TypeId {
TypeId::of::<Self>()
}
}
impl UserError {
/// Attempt to downcast this `UserError` to a concrete type by reference.
pub fn downcast_ref<T: UserError>(&self) -> Option<&T> {
if self.__private_get_type_id__() == TypeId::of::<T>() {
unsafe { Some(&*(self as *const UserError as *const T)) }
} else {
None
}
}
/// Attempt to downcast this `UserError` to a concrete type by mutable
/// reference.
pub fn downcast_mut<T: UserError>(&mut self) -> Option<&mut T> {
if self.__private_get_type_id__() == TypeId::of::<T>() {
unsafe { Some(&mut *(self as *mut UserError as *mut T)) }
} else {
None
}
}
}
/// Internal interpreter error.
#[derive(Debug)]
pub enum Error {
/// Program-level error.
Program(String),
/// Validation error.
Validation(String),
/// Initialization error.
Initialization(String),
/// Function-level error.
Function(String),
/// Table-level error.
Table(String),
/// Memory-level error.
Memory(String),
/// Variable-level error.
Variable(String),
/// Global-level error.
Global(String),
/// Local-level error.
Local(String),
/// Stack-level error.
Stack(String),
/// Value-level error.
Value(String),
/// Interpreter (code) error.
Interpreter(String),
/// Native module error.
Native(String),
/// Trap.
Trap(String),
/// Custom user error.
User(Box<UserError>),
}
impl Into<String> for Error {
fn into(self) -> String {
match self {
Error::Program(s) => s,
Error::Validation(s) => s,
Error::Initialization(s) => s,
Error::Function(s) => s,
Error::Table(s) => s,
Error::Memory(s) => s,
Error::Variable(s) => s,
Error::Global(s) => s,
Error::Local(s) => s,
Error::Stack(s) => s,
Error::Interpreter(s) => s,
Error::Value(s) => s,
Error::Native(s) => s,
Error::Trap(s) => format!("trap: {}", s),
Error::User(e) => format!("user: {}", e),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Program(ref s) => write!(f, "Program: {}", s),
Error::Validation(ref s) => write!(f, "Validation: {}", s),
Error::Initialization(ref s) => write!(f, "Initialization: {}", s),
Error::Function(ref s) => write!(f, "Function: {}", s),
Error::Table(ref s) => write!(f, "Table: {}", s),
Error::Memory(ref s) => write!(f, "Memory: {}", s),
Error::Variable(ref s) => write!(f, "Variable: {}", s),
Error::Global(ref s) => write!(f, "Global: {}", s),
Error::Local(ref s) => write!(f, "Local: {}", s),
Error::Stack(ref s) => write!(f, "Stack: {}", s),
Error::Interpreter(ref s) => write!(f, "Interpreter: {}", s),
Error::Value(ref s) => write!(f, "Value: {}", s),
Error::Native(ref s) => write!(f, "Native: {}", s),
Error::Trap(ref s) => write!(f, "Trap: {}", s),
Error::User(ref e) => write!(f, "User: {}", e),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Program(ref s) => s,
Error::Validation(ref s) => s,
Error::Initialization(ref s) => s,
Error::Function(ref s) => s,
Error::Table(ref s) => s,
Error::Memory(ref s) => s,
Error::Variable(ref s) => s,
Error::Global(ref s) => s,
Error::Local(ref s) => s,
Error::Stack(ref s) => s,
Error::Interpreter(ref s) => s,
Error::Value(ref s) => s,
Error::Native(ref s) => s,
Error::Trap(ref s) => s,
Error::User(_) => "User error",
}
}
}
impl<U> From<U> for Error where U: UserError + Sized {
fn from(e: U) -> Self {
Error::User(Box::new(e))
}
}
impl From<validation::Error> for Error {
fn from(e: validation::Error) -> Self {
Error::Validation(e.to_string())
}
}
impl From<common::stack::Error> for Error {
fn from(e: common::stack::Error) -> Self {
Error::Stack(e.to_string())
}
}
mod validator;
mod native;
mod imports;
mod memory;
mod module;
mod program;
mod runner;
mod stack;
mod table;
mod value;
mod variable;
#[cfg(test)]
mod tests;
pub use self::memory::MemoryInstance;
pub use self::module::{ModuleInstance, ModuleInstanceInterface,
ItemIndex, ExportEntryType, CallerContext, ExecutionParams, FunctionSignature};
pub use self::table::TableInstance;
pub use self::program::ProgramInstance;
pub use self::value::RuntimeValue;
pub use self::variable::{VariableInstance, VariableType, ExternalVariableValue};
pub use self::native::{native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor};

View File

@ -1,746 +0,0 @@
use std::collections::HashMap;
use std::iter::repeat;
use std::sync::{Arc, Weak};
use std::fmt;
use elements::{Module, InitExpr, Opcode, Type, FunctionType, Internal, External, ResizableLimits, Local, ValueType, BlockType};
use interpreter::Error;
use interpreter::native::UserFunctionDescriptor;
use interpreter::imports::ModuleImports;
use interpreter::memory::MemoryInstance;
use interpreter::program::ProgramInstanceEssence;
use interpreter::runner::{Interpreter, FunctionContext, prepare_function_args};
use interpreter::table::TableInstance;
use interpreter::validator::{Validator, FunctionValidationContext};
use interpreter::value::{RuntimeValue, TryInto};
use interpreter::variable::{VariableInstance, VariableType};
use common::stack::StackWithLimit;
/// Maximum number of entries in value stack.
const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
/// Maximum number of entries in frame stack.
const DEFAULT_FRAME_STACK_LIMIT: usize = 1024;
/// Execution context.
#[derive(Clone)]
pub struct ExecutionParams<'a> {
/// Arguments.
pub args: Vec<RuntimeValue>,
/// Execution-local external modules.
pub externals: HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
}
/// Export type.
#[derive(Debug, Clone)]
pub enum ExportEntryType<'a> {
/// Any type.
Any,
/// Type of function.
Function(FunctionSignature<'a>),
/// Type of global.
Global(VariableType),
}
/// Function signature.
#[derive(Debug, Clone)]
pub enum FunctionSignature<'a> {
/// Module function reference.
Module(&'a FunctionType),
/// Native user function refrence.
User(&'a UserFunctionDescriptor),
}
/// Module instance API.
pub trait ModuleInstanceInterface {
/// Execute function with the given index.
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>;
/// Execute function with the given export name.
fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>;
/// Get export entry.
fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error>;
/// Get table reference.
fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error>;
/// Get memory reference.
fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error>;
/// Get global reference.
fn global<'a>(&self, index: ItemIndex, variable_type: Option<VariableType>, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<VariableInstance>, Error>;
/// Get function type for given function index.
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionSignature, Error>;
/// Get function type for given function index.
fn function_type_by_index(&self, type_index: u32) -> Result<FunctionSignature, Error>;
/// Get function reference.
fn function_reference<'a>(&self, index: ItemIndex, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error>;
/// Get function indirect reference.
fn function_reference_indirect<'a>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error>;
/// Get internal function for interpretation.
fn function_body<'a>(&'a self, internal_index: u32) -> Result<Option<InternalFunction<'a>>, Error>;
/// Call function with given internal index.
fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, Error>;
}
/// Item index in items index space.
#[derive(Debug, Clone, Copy)]
pub enum ItemIndex {
/// Index in index space.
IndexSpace(u32),
/// Internal item index (i.e. index of item in items section).
Internal(u32),
/// External module item index (i.e. index of item in the import section).
External(u32),
}
/// Module instance.
pub struct ModuleInstance {
/// Module name.
name: String,
/// Module.
module: Module,
/// Function labels.
functions_labels: HashMap<u32, HashMap<usize, usize>>,
/// Module imports.
imports: ModuleImports,
/// Module exports.
exports: HashMap<String, Vec<Internal>>,
/// Tables.
tables: Vec<Arc<TableInstance>>,
/// Linear memory regions.
memory: Vec<Arc<MemoryInstance>>,
/// Globals.
globals: Vec<Arc<VariableInstance>>,
}
/// Caller context.
pub struct CallerContext<'a> {
/// Value stack limit
pub value_stack_limit: usize,
/// Frame stack limit
pub frame_stack_limit: usize,
/// Stack of the input parameters
pub value_stack: &'a mut StackWithLimit<RuntimeValue>,
/// Execution-local external modules.
pub externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
}
/// Internal function reference.
#[derive(Clone)]
pub struct InternalFunctionReference<'a> {
/// Module reference.
pub module: Arc<ModuleInstanceInterface + 'a>,
/// Internal function index.
pub internal_index: u32,
}
impl<'a> fmt::Debug for InternalFunctionReference<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "InternalFunctionReference")
}
}
/// Internal function ready for interpretation.
pub struct InternalFunction<'a> {
/// Function locals.
pub locals: &'a [Local],
/// Function body.
pub body: &'a [Opcode],
/// Function labels.
pub labels: &'a HashMap<usize, usize>,
}
impl<'a> ExecutionParams<'a> {
/// Create new execution params with given externa; module override.
pub fn with_external(name: String, module: Arc<ModuleInstanceInterface + 'a>) -> Self {
let mut externals = HashMap::new();
externals.insert(name, module);
ExecutionParams {
args: Vec::new(),
externals: externals,
}
}
/// Add argument.
pub fn add_argument(mut self, arg: RuntimeValue) -> Self {
self.args.push(arg);
self
}
}
impl<'a> Default for ExecutionParams<'a> {
fn default() -> Self {
ExecutionParams {
args: Vec::default(),
externals: HashMap::default(),
}
}
}
impl<'a> From<Vec<RuntimeValue>> for ExecutionParams<'a> {
fn from(args: Vec<RuntimeValue>) -> ExecutionParams<'a> {
ExecutionParams {
args: args,
externals: HashMap::new(),
}
}
}
impl ModuleInstance {
/// Instantiate given module within program context.
pub fn new<'a>(program: Weak<ProgramInstanceEssence>, name: String, module: Module) -> Result<Self, Error> {
// load entries from import section
let imports = ModuleImports::new(program, module.import_section());
// instantiate linear memory regions, if any
let memory = match module.memory_section() {
Some(memory_section) => memory_section.entries()
.iter()
.map(MemoryInstance::new)
.collect::<Result<Vec<_>, _>>()?,
None => Vec::new(),
};
// instantiate tables, if any
let tables = match module.table_section() {
Some(table_section) => table_section.entries()
.iter()
.map(|tt| TableInstance::new(tt))
.collect::<Result<Vec<_>, _>>()?,
None => Vec::new(),
};
// instantiate globals, if any
let globals = match module.global_section() {
Some(global_section) => global_section.entries()
.iter()
.map(|g| {
get_initializer(g.init_expr(), &module, &imports, g.global_type().content_type().into())
.map_err(|e| Error::Initialization(e.into()))
.and_then(|v| VariableInstance::new_global(g.global_type(), v).map(Arc::new))
})
.collect::<Result<Vec<_>, _>>()?,
None => Vec::new(),
};
Ok(ModuleInstance {
name: name,
module: module,
imports: imports,
exports: HashMap::new(),
functions_labels: HashMap::new(),
memory: memory,
tables: tables,
globals: globals,
})
}
/// Run instantiation-time procedures (validation). Module is not completely validated until this call.
pub fn instantiate<'a>(&mut self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> {
::validation::validate_module(&self.module)?;
// validate start section
if let Some(start_function) = self.module.start_section() {
let func_type_index = self.require_function(ItemIndex::IndexSpace(start_function))?;
let func_type = self.function_type_by_index(func_type_index)?;
if func_type.return_type() != None || func_type.params().len() != 0 {
return Err(Error::Validation("start function expected to have type [] -> []".into()));
}
}
// validate export section
if let Some(export_section) = self.module.export_section() {
for export in export_section.entries() {
match export.internal() {
&Internal::Function(function_index) => {
self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?;
self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index));
},
&Internal::Global(global_index) => {
self.global(ItemIndex::IndexSpace(global_index), None, externals)
.and_then(|g| if g.is_mutable() {
Err(Error::Validation(format!("trying to export mutable global {}", export.field())))
} else {
Ok(())
})?;
self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Global(global_index));
},
&Internal::Memory(memory_index) => {
self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?;
self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Memory(memory_index));
},
&Internal::Table(table_index) => {
self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?;
self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Table(table_index));
},
}
}
}
// validate import section
if let Some(import_section) = self.module.import_section() {
for import in import_section.entries() {
match import.external() {
// for functions we need to check if function type matches in both modules
&External::Function(ref function_type_index) => {
// External::Function points to function type in type section in this module
let import_function_type = self.function_type_by_index(*function_type_index)?;
// get export entry in external module
let external_module = self.imports.module(externals, import.module())?;
let export_entry = external_module.export_entry(import.field(), &ExportEntryType::Function(import_function_type.clone()))?;
// export entry points to function in function index space
// and Internal::Function points to type in type section
match export_entry {
Internal::Function(function_index) => {
external_module.function_type(ItemIndex::IndexSpace(function_index))?
}
_ => {
return Err(Error::Validation(format!(
"Export with name {} from module {} is not a function",
import.field(),
import.module()
)))
}
};
},
&External::Global(ref global_type) => if global_type.is_mutable() {
return Err(Error::Validation(format!("trying to import mutable global {}", import.field())));
} else {
self.imports.global(externals, import, Some(global_type.content_type().into()))?;
},
&External::Memory(ref memory_type) => {
let import_limits = memory_type.limits();
check_limits(import_limits)?;
let memory = self.imports.memory(externals, import)?;
let memory_limits = memory.limits();
// a linear-memory import's minimum length is required to be at most the imported linear memory's minimum length.
if import_limits.initial() > memory_limits.initial() {
return Err(Error::Validation(format!("trying to import memory with initial={} and import.initial={}", memory_limits.initial(), import_limits.initial())));
}
// not working because of wabt tests:
// a linear-memory import is required to have a maximum length if the imported linear memory has a maximum length.
// if present, a linear-memory import's maximum length is required to be at least the imported linear memory's maximum length.
match (memory_limits.maximum(), import_limits.maximum()) {
(Some(ml), Some(il)) if il < ml =>
return Err(Error::Validation(format!("trying to import memory with maximum={} and import.maximum={}", ml, il))),
_ => (),
}
},
&External::Table(ref table_type) => {
let import_limits = table_type.limits();
check_limits(import_limits)?;
let table = self.imports.table(externals, import)?;
let table_limits = table.limits();
// a table import's minimum length is required to be at most the imported table's minimum length.
if import_limits.initial() > table_limits.initial() {
return Err(Error::Validation(format!("trying to import table with initial={} and import.initial={}", table_limits.initial(), import_limits.initial())));
}
// not working because of wabt tests:
// a table import is required to have a maximum length if the imported table has a maximum length.
// if present, a table import's maximum length is required to be at least the imported table's maximum length.
match (table_limits.maximum(), import_limits.maximum()) {
(Some(ml), Some(il)) if il < ml =>
return Err(Error::Validation(format!("trying to import table with maximum={} and import.maximum={}", ml, il))),
_ => (),
}
},
}
}
}
// there must be no greater than 1 table in tables index space
if self.imports.tables_len() + self.tables.len() > 1 {
return Err(Error::Validation(format!("too many tables in index space: {}", self.imports.tables_len() + self.tables.len())));
}
// there must be no greater than 1 memory region in memory regions index space
if self.imports.memory_regions_len() + self.memory.len() > 1 {
return Err(Error::Validation(format!("too many memory regions in index space: {}", self.imports.memory_regions_len() + self.memory.len())));
}
// for every function section entry there must be corresponding entry in code section and type && vice versa
let function_section_len = self.module.function_section().map(|s| s.entries().len()).unwrap_or(0);
let code_section_len = self.module.code_section().map(|s| s.bodies().len()).unwrap_or(0);
if function_section_len != code_section_len {
return Err(Error::Validation(format!("length of function section is {}, while len of code section is {}", 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 = self.module.function_section().expect("function_section_len != 0; qed");
let code_section = self.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_labels = {
let function_type = self.function_type_by_index(function.type_ref())?;
let function_body = code_section.bodies().get(index as usize).ok_or(Error::Validation(format!("Missing body for function {}", index)))?;
let mut locals = function_type.params().to_vec();
locals.extend(function_body.locals().iter().flat_map(|l| repeat(l.value_type()).take(l.count() as usize)));
let mut context = FunctionValidationContext::new(
self,
externals,
&locals,
DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_STACK_LIMIT,
function_type.clone());
let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult);
Validator::validate_function(&mut context, block_type, function_body.code().elements())
.map_err(|e| {
if let Error::Validation(msg) = e {
Error::Validation(format!("Function #{} validation error: {}", index, msg))
} else {
e
}
})?;
context.function_labels()
};
self.functions_labels.insert(index as u32, function_labels);
}
}
// use data section to initialize linear memory regions
if let Some(data_section) = self.module.data_section() {
for (data_segment_index, data_segment) in data_section.entries().iter().enumerate() {
let offset: u32 = get_initializer(data_segment.offset(), &self.module, &self.imports, VariableType::I32)?.try_into()?;
self.memory(ItemIndex::IndexSpace(data_segment.index()))
.map_err(|e| Error::Initialization(format!("DataSegment {} initializes non-existant MemoryInstance {}: {:?}", data_segment_index, data_segment.index(), e)))
.and_then(|m| m.set(offset, data_segment.value()))
.map_err(|e| Error::Initialization(e.into()))?;
}
}
// use element section to fill tables
if let Some(element_section) = self.module.elements_section() {
for (element_segment_index, element_segment) in element_section.entries().iter().enumerate() {
let offset: u32 = get_initializer(element_segment.offset(), &self.module, &self.imports, VariableType::I32)?.try_into()?;
for function_index in element_segment.members() {
self.require_function(ItemIndex::IndexSpace(*function_index))?;
}
self.table(ItemIndex::IndexSpace(element_segment.index()))
.map_err(|e| Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}: {:?}", element_segment_index, element_segment.index(), e)))
.and_then(|m| m.set_raw(offset, self.name.clone(), element_segment.members()))
.map_err(|e| Error::Initialization(e.into()))?;
}
}
Ok(())
}
/// Run start function [if any].
pub fn run_start_function(&self) -> Result<(), Error> {
// execute start function (if any)
if let Some(start_function) = self.module.start_section() {
self.execute_index(start_function, ExecutionParams::default())?;
}
Ok(())
}
fn self_ref<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<ModuleInstanceInterface + 'a>, Error> {
self.imports.module(externals, &self.name)
}
fn require_function(&self, index: ItemIndex) -> Result<u32, Error> {
match self.imports.parse_function_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.module.function_section()
.ok_or(Error::Function(format!("missing internal function {}", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Function(format!("missing internal function {}", index))))
.map(|f| f.type_ref()),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Function(format!("missing external function {}", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Function(format!("missing external function {}", index))))
.and_then(|import| match import.external() {
&External::Function(type_idx) => Ok(type_idx),
_ => Err(Error::Function(format!("external function {} is pointing to non-function import", index))),
}),
}
}
}
impl ModuleInstanceInterface for ModuleInstance {
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
let ExecutionParams { args, externals } = params;
let mut args = StackWithLimit::with_data(args, DEFAULT_VALUE_STACK_LIMIT);
let function_reference = self.function_reference(ItemIndex::IndexSpace(index), Some(&externals))?;
let function_context = CallerContext::topmost(&mut args, &externals);
function_reference.module.call_internal_function(function_context, function_reference.internal_index)
}
fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
let index = self.exports.get(name)
.ok_or(Error::Function(format!("missing executable export with name {}", name)))
.and_then(|l| l.iter()
.find(|i| match i {
&&Internal::Function(_) => true,
_ => false,
})
.ok_or(Error::Function(format!("missing exported function with name {}", name)))
.map(|i| match i {
&Internal::Function(index) => index,
_ => unreachable!(), // checked couple of lines above
})
)?;
self.execute_index(index, params)
}
fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error> {
self.exports.get(name)
.ok_or(Error::Function(format!("missing export entry with name {}", name)))
.and_then(|l| l.iter()
.find(|i| match required_type {
&ExportEntryType::Any => true,
&ExportEntryType::Global(global_type) => match i {
&&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type), None).map(|_| true).unwrap_or(false),
_ => false,
},
&ExportEntryType::Function(ref required_type) => match i {
&&Internal::Function(function_index) =>
self.function_type(ItemIndex::IndexSpace(function_index))
.map(|ft| ft == *required_type)
.unwrap_or(false),
_ => false,
},
})
.map(|i| *i)
.ok_or(Error::Program(format!("unresolved export {}", name))))
}
fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error> {
match self.imports.parse_table_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_table_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.tables.get(index as usize).cloned()
.ok_or(Error::Table(format!("trying to access table with local index {} when there are only {} local tables", index, self.tables.len()))),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Table(format!("trying to access external table with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Table(format!("trying to access external table with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| self.imports.table(None, e)),
}
}
fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error> {
match self.imports.parse_memory_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_memory_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.memory.get(index as usize).cloned()
.ok_or(Error::Memory(format!("trying to access memory with local index {} when there are only {} memory regions", index, self.memory.len()))),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Memory(format!("trying to access external memory with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Memory(format!("trying to access external memory with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| self.imports.memory(None, e)),
}
}
fn global<'a>(&self, index: ItemIndex, variable_type: Option<VariableType>, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<VariableInstance>, Error> {
match self.imports.parse_global_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_global_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.globals.get(index as usize).cloned()
.ok_or(Error::Global(format!("trying to access global with local index {} when there are only {} globals", index, self.globals.len()))),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Global(format!("trying to access external global with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Global(format!("trying to access external global with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| self.imports.global(externals, e, variable_type)),
}
}
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionSignature, Error> {
match self.imports.parse_function_index(function_index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.require_function(ItemIndex::Internal(index))
.and_then(|ft| self.function_type_by_index(ft)),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Function(format!("trying to access external function with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Function(format!("trying to access external function with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Function(type_index) => self.function_type_by_index(type_index),
_ => Err(Error::Function(format!("exported function {} is not a function", index))),
}),
}
}
fn function_type_by_index(&self, type_index: u32) -> Result<FunctionSignature, Error> {
self.module.type_section()
.ok_or(Error::Validation(format!("type reference {} exists in module without type section", type_index)))
.and_then(|s| match s.types().get(type_index as usize) {
Some(&Type::Function(ref function_type)) => Ok(function_type),
_ => Err(Error::Validation(format!("missing function type with index {}", type_index))),
})
.map(FunctionSignature::Module)
}
fn function_reference<'a>(&self, index: ItemIndex, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error> {
match self.imports.parse_function_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
ItemIndex::Internal(index) => Ok(InternalFunctionReference {
module: self.self_ref(externals)?,
internal_index: index,
}),
ItemIndex::External(index) => {
let import_entry = self.module.import_section()
.expect("parse_function_index has returned External(index); it is only returned when import section exists; qed")
.entries().get(index as usize)
.expect("parse_function_index has returned External(index); it is only returned when entry with index exists in import section exists; qed");
let required_function_type = self.function_type(ItemIndex::External(index))?;
let internal_function_index = self.imports.function(externals, import_entry, Some(required_function_type))?;
Ok(InternalFunctionReference {
module: self.imports.module(externals, import_entry.module())?,
internal_index: internal_function_index,
})
},
}
}
fn function_reference_indirect<'a>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error> {
let table = self.table(ItemIndex::IndexSpace(table_idx))?;
let (module, index) = match table.get(func_idx)? {
RuntimeValue::AnyFunc(module, index) => (module.clone(), index),
_ => return Err(Error::Function(format!("trying to indirect call function {} via non-anyfunc table {:?}", func_idx, table_idx))),
};
let module = self.imports.module(externals, &module)?;
let required_function_type = self.function_type_by_index(type_idx)?;
let actual_function_type = module.function_type(ItemIndex::IndexSpace(index))?;
if required_function_type != actual_function_type {
return Err(Error::Function(format!("expected indirect function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}",
required_function_type.params(), required_function_type.return_type(),
actual_function_type.params(), actual_function_type.return_type())));
}
module.function_reference(ItemIndex::IndexSpace(index), externals)
}
fn function_body<'a>(&'a self, internal_index: u32) -> Result<Option<InternalFunction<'a>>, Error> {
let function_body = self.module
.code_section()
.ok_or(Error::Function(format!("trying to call function with index {} in module without code section", internal_index)))
.and_then(|s| s.bodies()
.get(internal_index as usize)
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", internal_index, s.bodies().len()))))?;
let function_labels = self.functions_labels.get(&internal_index)
.ok_or(Error::Function(format!("trying to call non-validated internal function {}", internal_index)))?;
Ok(Some(InternalFunction {
locals: function_body.locals(),
body: function_body.code().elements(),
labels: function_labels,
}))
}
fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, Error> {
let function_type = self.function_type(ItemIndex::Internal(index))?;
let args = prepare_function_args(&function_type, outer.value_stack)?;
let function_ref = InternalFunctionReference { module: self.self_ref(Some(outer.externals))?, internal_index: index };
let inner = FunctionContext::new(function_ref, outer.externals, outer.value_stack_limit, outer.frame_stack_limit, &function_type, args);
Interpreter::run_function(inner)
}
}
impl<'a> CallerContext<'a> {
/// Top most args
pub fn topmost(args: &'a mut StackWithLimit<RuntimeValue>, externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>) -> Self {
CallerContext {
value_stack_limit: DEFAULT_VALUE_STACK_LIMIT,
frame_stack_limit: DEFAULT_FRAME_STACK_LIMIT,
value_stack: args,
externals: externals,
}
}
/// Nested context
pub fn nested(outer: &'a mut FunctionContext) -> Self {
CallerContext {
value_stack_limit: outer.value_stack().limit() - outer.value_stack().len(),
frame_stack_limit: outer.frame_stack().limit() - outer.frame_stack().len(),
value_stack: &mut outer.value_stack,
externals: &outer.externals,
}
}
}
pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> {
if let Some(maximum) = limits.maximum() {
if maximum < limits.initial() {
return Err(Error::Validation(format!("maximum limit {} is lesser than minimum {}", maximum, limits.initial())));
}
}
Ok(())
}
fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports, expected_type: VariableType) -> Result<RuntimeValue, Error> {
let first_opcode = match expr.code().len() {
1 => &expr.code()[0],
2 if expr.code().len() == 2 && expr.code()[1] == Opcode::End => &expr.code()[0],
_ => return Err(Error::Initialization(format!("expected 1-instruction len initializer. Got {:?}", expr.code()))),
};
match first_opcode {
&Opcode::GetGlobal(index) => {
let index = match imports.parse_global_index(ItemIndex::IndexSpace(index)) {
ItemIndex::External(index) => index,
_ => return Err(Error::Global(format!("trying to initialize with non-external global {}", index))),
};
module.import_section()
.ok_or(Error::Global(format!("trying to initialize with external global with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Global(format!("trying to initialize with external global with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| imports.global(None, e, Some(expected_type)))
.map(|g| g.get())
},
&Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)),
&Opcode::I64Const(val) => Ok(RuntimeValue::I64(val)),
&Opcode::F32Const(val) => Ok(RuntimeValue::decode_f32(val)),
&Opcode::F64Const(val) => Ok(RuntimeValue::decode_f64(val)),
_ => Err(Error::Initialization(format!("not-supported {:?} instruction in instantiation-time initializer", first_opcode))),
}
}
impl<'a> FunctionSignature<'a> {
/// Get return type of this function.
pub fn return_type(&self) -> Option<ValueType> {
match self {
&FunctionSignature::Module(ft) => ft.return_type(),
&FunctionSignature::User(fd) => fd.return_type(),
}
}
/// Get parameters of this function.
pub fn params(&self) -> &[ValueType] {
match self {
&FunctionSignature::Module(ft) => ft.params(),
&FunctionSignature::User(fd) => fd.params(),
}
}
}
impl<'a> PartialEq for FunctionSignature<'a> {
fn eq<'b>(&self, other: &FunctionSignature<'b>) -> bool {
match self {
&FunctionSignature::Module(ft1) => match other {
&FunctionSignature::Module(ft2) => ft1 == ft2,
&FunctionSignature::User(ft2) => ft1.params() == ft2.params() && ft1.return_type() == ft2.return_type(),
},
&FunctionSignature::User(ft1) => match other {
&FunctionSignature::User(ft2) => ft1 == ft2,
&FunctionSignature::Module(ft2) => ft1.params() == ft2.params() && ft1.return_type() == ft2.return_type(),
},
}
}
}
impl<'a> From<&'a FunctionType> for FunctionSignature<'a> {
fn from(other: &'a FunctionType) -> Self {
FunctionSignature::Module(other)
}
}

View File

@ -1,288 +0,0 @@
use std::sync::Arc;
use std::collections::HashMap;
use std::borrow::Cow;
use parking_lot::RwLock;
use elements::{Internal, ValueType};
use interpreter::Error;
use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex,
CallerContext, ExportEntryType, InternalFunctionReference, InternalFunction, FunctionSignature};
use interpreter::memory::MemoryInstance;
use interpreter::table::TableInstance;
use interpreter::value::RuntimeValue;
use interpreter::variable::{VariableInstance, VariableType};
/// Min index of native function.
pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001;
/// Min index of native global.
pub const NATIVE_INDEX_GLOBAL_MIN: u32 = 20001;
/// User functions executor.
pub trait UserFunctionExecutor {
/// Execute function with given name.
fn execute(&mut self, name: &str, context: CallerContext) -> Result<Option<RuntimeValue>, Error>;
}
/// User function descriptor
#[derive(Debug, Clone)]
pub enum UserFunctionDescriptor {
/// Static function definition
Static(&'static str, &'static [ValueType], Option<ValueType>),
/// Dynamic heap function definition
Heap(String, Vec<ValueType>, Option<ValueType>),
}
impl UserFunctionDescriptor {
/// New function with statically known params
pub fn statik(name: &'static str, params: &'static [ValueType], result: Option<ValueType>) -> Self {
UserFunctionDescriptor::Static(name, params, result)
}
/// New function with statically unknown params
pub fn heap(name: String, params: Vec<ValueType>, result: Option<ValueType>) -> Self {
UserFunctionDescriptor::Heap(name, params, result)
}
/// Name of the function
pub fn name(&self) -> &str {
match self {
&UserFunctionDescriptor::Static(name, _, _) => name,
&UserFunctionDescriptor::Heap(ref name, _, _) => name,
}
}
/// Arguments of the function
pub fn params(&self) -> &[ValueType] {
match self {
&UserFunctionDescriptor::Static(_, params, _) => params,
&UserFunctionDescriptor::Heap(_, ref params, _) => params,
}
}
/// Return type of the function
pub fn return_type(&self) -> Option<ValueType> {
match self {
&UserFunctionDescriptor::Static(_, _, result) => result,
&UserFunctionDescriptor::Heap(_, _, result) => result,
}
}
}
/// Set of user-defined module elements.
pub struct UserDefinedElements<E: UserFunctionExecutor> {
/// User globals list.
pub globals: HashMap<String, Arc<VariableInstance>>,
/// User functions list.
pub functions: Cow<'static, [UserFunctionDescriptor]>,
/// Functions executor.
pub executor: Option<E>,
}
/// Native module instance.
pub struct NativeModuleInstance<E: UserFunctionExecutor> {
/// Underlying module reference.
base: Arc<ModuleInstanceInterface>,
/// User function executor.
executor: RwLock<Option<E>>,
/// By-name functions index.
functions_by_name: HashMap<String, u32>,
/// User functions list.
functions: Cow<'static, [UserFunctionDescriptor]>,
/// By-name functions index.
globals_by_name: HashMap<String, u32>,
/// User globals list.
globals: Vec<Arc<VariableInstance>>,
}
impl<E: UserFunctionExecutor> NativeModuleInstance<E> {
/// Create new native module
pub fn new(base: Arc<ModuleInstanceInterface>, elements: UserDefinedElements<E>) -> Result<Self, Error> {
if !elements.functions.is_empty() && elements.executor.is_none() {
return Err(Error::Function("trying to construct native module with functions, but without executor".into()));
}
Ok(NativeModuleInstance {
base: base,
executor: RwLock::new(elements.executor),
functions_by_name: elements.functions.iter().enumerate().map(|(i, f)| (f.name().to_owned(), i as u32)).collect(),
functions: elements.functions,
globals_by_name: elements.globals.iter().enumerate().map(|(i, (g_name, _))| (g_name.to_owned(), i as u32)).collect(),
globals: elements.globals.into_iter().map(|(_, g)| g).collect(),
})
}
}
impl<E: UserFunctionExecutor> ModuleInstanceInterface for NativeModuleInstance<E> {
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
self.base.execute_index(index, params)
}
fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
self.base.execute_export(name, params)
}
fn export_entry<'b>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error> {
if let Some(index) = self.functions_by_name.get(name) {
let composite_index = NATIVE_INDEX_FUNC_MIN + *index;
match required_type {
&ExportEntryType::Any => return Ok(Internal::Function(composite_index)),
&ExportEntryType::Function(ref required_type) => {
let actual_type = self.function_type(ItemIndex::Internal(composite_index))
.expect(
"by_name contains index; function_type succeeds for all functions from by_name; qed",
);
return if actual_type == *required_type {
Ok(Internal::Function(composite_index))
} else {
Err(Error::Validation(format!(
"Export function type {} mismatch. Expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}",
index,
required_type.params(),
required_type.return_type(),
actual_type.params(),
actual_type.return_type()
)))
};
}
_ => (),
}
}
if let Some(index) = self.globals_by_name.get(name) {
let composite_index = NATIVE_INDEX_GLOBAL_MIN + *index;
match required_type {
&ExportEntryType::Any => {
return Ok(Internal::Global(composite_index))
}
&ExportEntryType::Global(ref required_type) => {
let actual_type = self.globals
.get(*index as usize)
.expect(
"globals_by_name maps to indexes of globals; index read from globals_by_name; qed",
)
.variable_type();
return if actual_type == *required_type {
Ok(Internal::Global(composite_index))
} else {
Err(Error::Validation(format!(
"Export global type {} mismatch. Expected type {:?} when got {:?}",
index,
required_type,
actual_type
)))
};
}
_ => (),
}
}
self.base.export_entry(name, required_type)
}
fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error> {
self.base.table(index)
}
fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error> {
self.base.memory(index)
}
fn global<'b>(&self, global_index: ItemIndex, variable_type: Option<VariableType>, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<Arc<VariableInstance>, Error> {
let index = match global_index {
ItemIndex::IndexSpace(index) | ItemIndex::Internal(index) => index,
ItemIndex::External(_) => unreachable!("trying to get global, exported by native module"),
};
if index < NATIVE_INDEX_GLOBAL_MIN {
return self.base.global(global_index, variable_type, externals);
}
self.globals
.get((index - NATIVE_INDEX_GLOBAL_MIN) as usize)
.cloned()
.ok_or(Error::Native(format!("trying to get native global with index {}", index)))
}
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionSignature, Error> {
let index = match function_index {
ItemIndex::IndexSpace(index) | ItemIndex::Internal(index) => index,
ItemIndex::External(_) => unreachable!("trying to call function, exported by native module"),
};
if index < NATIVE_INDEX_FUNC_MIN || index >= NATIVE_INDEX_GLOBAL_MIN {
return self.base.function_type(function_index);
}
Ok(FunctionSignature::User(self.functions
.get((index - NATIVE_INDEX_FUNC_MIN) as usize)
.ok_or(Error::Native(format!("missing native function with index {}", index)))?))
}
fn function_type_by_index(&self, type_index: u32) -> Result<FunctionSignature, Error> {
self.function_type(ItemIndex::Internal(type_index))
}
fn function_reference<'b>(&self, index: ItemIndex, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<InternalFunctionReference<'b>, Error> {
self.base.function_reference(index, externals)
}
fn function_reference_indirect<'b>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<InternalFunctionReference<'b>, Error> {
self.base.function_reference_indirect(table_idx, type_idx, func_idx, externals)
}
fn function_body<'b>(&'b self, _internal_index: u32) -> Result<Option<InternalFunction<'b>>, Error> {
Ok(None)
}
fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, Error> {
if index < NATIVE_INDEX_FUNC_MIN || index >= NATIVE_INDEX_GLOBAL_MIN {
return self.base.call_internal_function(outer, index);
}
self.functions
.get((index - NATIVE_INDEX_FUNC_MIN) as usize)
.ok_or(Error::Native(format!("trying to call native function with index {}", index)).into())
.and_then(|f| self.executor.write()
.as_mut()
.expect("function exists; if function exists, executor must also exists [checked in constructor]; qed")
.execute(&f.name(), outer))
}
}
/// Create wrapper for a module with given native user functions.
///
/// # Examples
///
/// ```rust
/// use parity_wasm::interpreter::{CallerContext, Error, RuntimeValue, UserFunctionExecutor};
///
/// struct MyExecutor;
///
/// impl UserFunctionExecutor for MyExecutor {
/// fn execute(
/// &mut self,
/// name: &str,
/// context: CallerContext,
/// ) -> Result<Option<RuntimeValue>, Error> {
/// match name {
/// "add" => {
/// // fn add(a: u32, b: u32) -> u32
/// let b = context.value_stack.pop_as::<u32>()?;
/// let a = context.value_stack.pop_as::<u32>()?;
/// let sum = a + b;
/// Ok(Some(RuntimeValue::I32(sum as i32)))
/// }
/// _ => Err(Error::Trap("not implemented".into()).into()),
/// }
/// }
/// }
/// ```
pub fn native_module<'a, E: UserFunctionExecutor + 'a>(base: Arc<ModuleInstanceInterface>, user_elements: UserDefinedElements<E>) -> Result<Arc<ModuleInstanceInterface + 'a>, Error> {
Ok(Arc::new(NativeModuleInstance::new(base, user_elements)?))
}
impl<'a> PartialEq for UserFunctionDescriptor {
fn eq(&self, other: &Self) -> bool {
self.params() == other.params()
&& self.return_type() == other.return_type()
}
}

View File

@ -1,64 +0,0 @@
use std::sync::Arc;
use std::collections::HashMap;
use parking_lot::RwLock;
use elements::Module;
use interpreter::Error;
use interpreter::module::{ModuleInstance, ModuleInstanceInterface};
/// Program instance. Program is a set of instantiated modules.
pub struct ProgramInstance {
/// Shared data reference.
essence: Arc<ProgramInstanceEssence>,
}
/// Program instance essence.
pub struct ProgramInstanceEssence {
/// Loaded modules.
modules: RwLock<HashMap<String, Arc<ModuleInstanceInterface>>>,
}
impl ProgramInstance {
/// Create new program instance.
pub fn new() -> Self {
ProgramInstance {
essence: Arc::new(ProgramInstanceEssence::new()),
}
}
/// Instantiate module with validation.
pub fn add_module<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<ModuleInstance>, Error> {
let mut module_instance = ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?;
module_instance.instantiate(externals)?;
let module_instance = Arc::new(module_instance);
self.essence.modules.write().insert(name.into(), module_instance.clone());
module_instance.run_start_function()?;
Ok(module_instance)
}
/// Insert instantiated module.
pub fn insert_loaded_module(&self, name: &str, module_instance: Arc<ModuleInstanceInterface>) -> Result<Arc<ModuleInstanceInterface>, Error> {
// replace existing module with the same name with new one
self.essence.modules.write().insert(name.into(), Arc::clone(&module_instance));
Ok(module_instance)
}
/// Get one of the modules by name
pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface>> {
self.essence.module(name)
}
}
impl ProgramInstanceEssence {
/// Create new program essence.
pub fn new() -> Self {
ProgramInstanceEssence {
modules: RwLock::new(HashMap::new()),
}
}
/// Get module reference.
pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface>> {
self.modules.read().get(name).cloned()
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
use interpreter::{Error as InterpreterError};
use interpreter::value::{RuntimeValue, TryInto};
use common::stack::StackWithLimit;
impl StackWithLimit<RuntimeValue> {
pub fn pop_as<T>(&mut self) -> Result<T, InterpreterError>
where
RuntimeValue: TryInto<T, InterpreterError>,
{
let value = self.pop()?;
TryInto::try_into(value)
}
pub fn pop_pair(&mut self) -> Result<(RuntimeValue, RuntimeValue), InterpreterError> {
let right = self.pop()?;
let left = self.pop()?;
Ok((left, right))
}
pub fn pop_pair_as<T>(&mut self) -> Result<(T, T), InterpreterError>
where
RuntimeValue: TryInto<T, InterpreterError>,
{
let right = self.pop_as()?;
let left = self.pop_as()?;
Ok((left, right))
}
pub fn pop_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), InterpreterError> {
let right = self.pop()?;
let mid = self.pop()?;
let left = self.pop()?;
Ok((left, mid, right))
}
}

View File

@ -1,94 +0,0 @@
use std::u32;
use std::sync::Arc;
use parking_lot::RwLock;
use elements::{TableType, ResizableLimits};
use interpreter::Error;
use interpreter::module::check_limits;
use interpreter::variable::{VariableInstance, VariableType};
use interpreter::value::RuntimeValue;
/// Table instance.
pub struct TableInstance {
/// Table limits.
limits: ResizableLimits,
/// Table variables type.
variable_type: VariableType,
/// Table memory buffer.
buffer: RwLock<Vec<TableElement>>,
}
/// Table element. Cloneable wrapper around VariableInstance.
struct TableElement {
pub var: VariableInstance,
}
impl TableInstance {
/// New instance of the table
pub fn new(table_type: &TableType) -> Result<Arc<Self>, Error> {
check_limits(table_type.limits())?;
let variable_type = table_type.elem_type().into();
Ok(Arc::new(TableInstance {
limits: table_type.limits().clone(),
variable_type: variable_type,
buffer: RwLock::new(
vec![TableElement::new(VariableInstance::new(true, variable_type, RuntimeValue::Null)?); table_type.limits().initial() as usize]
),
}))
}
/// Return table limits.
pub fn limits(&self) -> &ResizableLimits {
&self.limits
}
/// Get variable type for this table.
pub fn variable_type(&self) -> VariableType {
self.variable_type
}
/// Get the specific value in the table
pub fn get(&self, offset: u32) -> Result<RuntimeValue, Error> {
let buffer = self.buffer.read();
let buffer_len = buffer.len();
buffer.get(offset as usize)
.map(|v| v.var.get())
.ok_or(Error::Table(format!("trying to read table item with index {} when there are only {} items", offset, buffer_len)))
}
/// Set the table value from raw slice
pub fn set_raw(&self, mut offset: u32, module_name: String, value: &[u32]) -> Result<(), Error> {
for val in value {
match self.variable_type {
VariableType::AnyFunc => self.set(offset, RuntimeValue::AnyFunc(module_name.clone(), *val))?,
_ => return Err(Error::Table(format!("table of type {:?} is not supported", self.variable_type))),
}
offset += 1;
}
Ok(())
}
/// Set the table from runtime variable value
pub fn set(&self, offset: u32, value: RuntimeValue) -> Result<(), Error> {
let mut buffer = self.buffer.write();
let buffer_len = buffer.len();
buffer.get_mut(offset as usize)
.ok_or(Error::Table(format!("trying to update table item with index {} when there are only {} items", offset, buffer_len)))
.and_then(|v| v.var.set(value))
}
}
impl TableElement {
pub fn new(var: VariableInstance) -> Self {
TableElement {
var: var,
}
}
}
impl Clone for TableElement {
fn clone(&self) -> Self {
TableElement::new(VariableInstance::new(self.var.is_mutable(), self.var.variable_type(), self.var.get())
.expect("it only fails when variable_type() != passed variable value; both are read from already constructed var; qed"))
}
}

View File

@ -1,608 +0,0 @@
///! Basic tests for instructions/constructions, missing in wabt tests
use std::sync::Arc;
use std::cell::RefCell;
use std::collections::HashMap;
use builder::module;
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
InitExpr, ValueType, Opcodes, Opcode, FunctionType, TableType, MemoryType};
use interpreter::{Error, UserError, ProgramInstance};
use interpreter::native::{native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor};
use interpreter::memory::MemoryInstance;
use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType, FunctionSignature};
use interpreter::value::{RuntimeValue, TryInto};
use interpreter::variable::{VariableInstance, ExternalVariableValue, VariableType};
use super::utils::program_with_default_env;
#[test]
fn import_function() {
let module1 = module()
.with_export(ExportEntry::new("external_func".into(), Internal::Function(0)))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::I32Const(3),
Opcode::End,
])).build()
.build()
.build();
let module2 = module()
.with_import(ImportEntry::new("external_module".into(), "external_func".into(), External::Function(0)))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::Call(0),
Opcode::I32Const(7),
Opcode::I32Add,
Opcode::End,
])).build()
.build()
.build();
let program = ProgramInstance::new();
let external_module = program.add_module("external_module", module1, None).unwrap();
let main_module = program.add_module("main", module2, None).unwrap();
assert_eq!(external_module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(3));
assert_eq!(main_module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(10));
}
#[test]
fn wrong_import() {
let side_module = module()
.with_export(ExportEntry::new("cool_func".into(), Internal::Function(0)))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::I32Const(3),
Opcode::End,
])).build()
.build()
.build();
let module = module()
.with_import(ImportEntry::new("side_module".into(), "not_cool_func".into(), External::Function(0)))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::Call(0),
Opcode::I32Const(7),
Opcode::I32Add,
Opcode::End,
])).build()
.build()
.build();
let program = ProgramInstance::new();
let _side_module_instance = program.add_module("side_module", side_module, None).unwrap();
assert!(program.add_module("main", module, None).is_err());
}
#[test]
fn global_get_set() {
let module = module()
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(42), Opcode::End])))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::GetGlobal(0),
Opcode::I32Const(8),
Opcode::I32Add,
Opcode::SetGlobal(0),
Opcode::GetGlobal(0),
Opcode::End,
])).build()
.build()
.build();
let program = ProgramInstance::new();
let module = program.add_module("main", module, None).unwrap();
assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(50));
assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(58));
}
const SIGNATURE_I32_I32: &'static [ValueType] = &[ValueType::I32, ValueType::I32];
const SIGNATURES: &'static [UserFunctionDescriptor] = &[
UserFunctionDescriptor::Static(
"add",
SIGNATURE_I32_I32,
Some(ValueType::I32),
),
UserFunctionDescriptor::Static(
"sub",
SIGNATURE_I32_I32,
Some(ValueType::I32),
),
UserFunctionDescriptor::Static(
"err",
SIGNATURE_I32_I32,
Some(ValueType::I32),
),
];
const NO_SIGNATURES: &'static [UserFunctionDescriptor] = &[];
// 'external' variable
struct MeasuredVariable {
pub val: i32,
}
impl ExternalVariableValue for MeasuredVariable {
fn get(&self) -> RuntimeValue {
RuntimeValue::I32(self.val)
}
fn set(&mut self, val: RuntimeValue) -> Result<(), Error> {
self.val = val.try_into()?;
Ok(())
}
}
// custom user error
#[derive(Debug, Clone, PartialEq)]
struct UserErrorWithCode {
error_code: i32,
}
impl ::std::fmt::Display for UserErrorWithCode {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
write!(f, "{}", self.error_code)
}
}
impl UserError for UserErrorWithCode {}
// user function executor
struct FunctionExecutor {
pub memory: Arc<MemoryInstance>,
pub values: Vec<i32>,
}
impl<'a> UserFunctionExecutor for &'a mut FunctionExecutor {
fn execute(&mut self, name: &str, context: CallerContext) -> Result<Option<RuntimeValue>, Error> {
match name {
"add" => {
let memory_value = self.memory.get(0, 1).unwrap()[0];
let fn_argument_unused = context.value_stack.pop_as::<u32>().unwrap() as u8;
let fn_argument = context.value_stack.pop_as::<u32>().unwrap() as u8;
assert_eq!(fn_argument_unused, 0);
let sum = memory_value + fn_argument;
self.memory.set(0, &vec![sum]).unwrap();
self.values.push(sum as i32);
Ok(Some(RuntimeValue::I32(sum as i32)))
},
"sub" => {
let memory_value = self.memory.get(0, 1).unwrap()[0];
let fn_argument_unused = context.value_stack.pop_as::<u32>().unwrap() as u8;
let fn_argument = context.value_stack.pop_as::<u32>().unwrap() as u8;
assert_eq!(fn_argument_unused, 0);
let diff = memory_value - fn_argument;
self.memory.set(0, &vec![diff]).unwrap();
self.values.push(diff as i32);
Ok(Some(RuntimeValue::I32(diff as i32)))
},
"err" => {
Err(Error::User(Box::new(UserErrorWithCode { error_code: 777 })))
},
_ => Err(Error::Trap("not implemented".into()).into()),
}
}
}
#[test]
fn native_env_function() {
// create new program
let program = program_with_default_env();
// => env module is created
let env_instance = program.module("env").unwrap();
// => linear memory is created
let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap();
// create native env module executor
let mut executor = FunctionExecutor {
memory: env_memory.clone(),
values: Vec::new(),
};
{
let functions = UserDefinedElements {
executor: Some(&mut executor),
globals: HashMap::new(),
functions: ::std::borrow::Cow::from(SIGNATURES),
};
let native_env_instance = native_module(env_instance, functions).unwrap();
let params = ExecutionParams::with_external("env".into(), native_env_instance);
let module = module()
.with_import(ImportEntry::new("env".into(), "add".into(), External::Function(0)))
.with_import(ImportEntry::new("env".into(), "sub".into(), External::Function(0)))
.function()
.signature().param().i32().param().i32().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::GetLocal(0),
Opcode::GetLocal(1),
Opcode::Call(0),
Opcode::End,
])).build()
.build()
.function()
.signature().param().i32().param().i32().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::GetLocal(0),
Opcode::GetLocal(1),
Opcode::Call(1),
Opcode::End,
])).build()
.build()
.build();
// load module
let module_instance = program.add_module("main", module, Some(&params.externals)).unwrap();
{
assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(7)).add_argument(RuntimeValue::I32(0))).unwrap().unwrap(), RuntimeValue::I32(7));
assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(50)).add_argument(RuntimeValue::I32(0))).unwrap().unwrap(), RuntimeValue::I32(57));
assert_eq!(module_instance.execute_index(3, params.clone().add_argument(RuntimeValue::I32(15)).add_argument(RuntimeValue::I32(0))).unwrap().unwrap(), RuntimeValue::I32(42));
}
}
assert_eq!(executor.memory.get(0, 1).unwrap()[0], 42);
assert_eq!(executor.values, vec![7, 57, 42]);
}
#[test]
fn native_env_function_own_memory() {
// create program + env module is auto instantiated + env module memory is instantiated (we do not need this)
let program = program_with_default_env();
struct OwnMemoryReference {
pub memory: RefCell<Option<Arc<MemoryInstance>>>,
}
struct OwnMemoryExecutor {
pub memory_ref: Arc<OwnMemoryReference>,
}
impl<'a> UserFunctionExecutor for &'a mut OwnMemoryExecutor {
fn execute(&mut self, name: &str, context: CallerContext) -> Result<Option<RuntimeValue>, Error> {
match name {
"add" => {
let memory = self.memory_ref.memory.borrow_mut().as_ref().expect("initialized before execution; qed").clone();
let memory_value = memory.get(0, 1).unwrap()[0];
let fn_argument_unused = context.value_stack.pop_as::<u32>().unwrap() as u8;
let fn_argument = context.value_stack.pop_as::<u32>().unwrap() as u8;
assert_eq!(fn_argument_unused, 0);
let sum = memory_value + fn_argument;
memory.set(0, &vec![sum]).unwrap();
Ok(Some(RuntimeValue::I32(sum as i32)))
},
_ => Err(Error::Trap("not implemented".into()).into()),
}
}
}
let env_instance = program.module("env").unwrap();
let memory_ref = Arc::new(OwnMemoryReference { memory: RefCell::new(None) });
let mut executor = OwnMemoryExecutor { memory_ref: memory_ref.clone() };
let native_env_instance = native_module(env_instance, UserDefinedElements {
executor: Some(&mut executor),
globals: HashMap::new(),
functions: ::std::borrow::Cow::from(SIGNATURES),
}).unwrap();
let params = ExecutionParams::with_external("env".into(), native_env_instance);
// create module definition with its own memory
// => since we do not import env' memory, all instructions from this module will access this memory
let module = module()
.memory().build() // new memory is created
.with_import(ImportEntry::new("env".into(), "add".into(), External::Function(0))) // import 'native' function
.function() // add simple wasm function
.signature().param().i32().param().i32().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::GetLocal(0),
Opcode::GetLocal(1),
Opcode::Call(0),
Opcode::End,
])).build()
.build()
.build();
// instantiate module
let module_instance = program.add_module("main", module, Some(&params.externals)).unwrap();
// now get memory reference
let module_memory = module_instance.memory(ItemIndex::Internal(0)).unwrap();
// post-initialize our executor with memory reference
*memory_ref.memory.borrow_mut() = Some(module_memory);
// now execute function => executor updates memory
assert_eq!(module_instance.execute_index(1, params.clone().add_argument(RuntimeValue::I32(7)).add_argument(RuntimeValue::I32(0))).unwrap().unwrap(),
RuntimeValue::I32(7));
}
#[test]
fn native_env_global() {
struct DummyExecutor;
impl UserFunctionExecutor for DummyExecutor {
fn execute(&mut self, _name: &str, _context: CallerContext) -> Result<Option<RuntimeValue>, Error> {
// this code should be unreachable, because we actually doesn't call any
// native functions in this test.
unreachable!();
}
}
let module_constructor = |elements: UserDefinedElements<DummyExecutor>| {
let program = program_with_default_env();
let env_instance = program.module("env").unwrap();
let native_env_instance = native_module(env_instance, elements).unwrap();
let params = ExecutionParams::with_external("env".into(), native_env_instance);
let module = module()
.with_import(ImportEntry::new("env".into(), "ext_global".into(), External::Global(GlobalType::new(ValueType::I32, false))))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::GetGlobal(0),
Opcode::End,
])).build()
.build()
.build();
program.add_module("main", module, Some(&params.externals))?
.execute_index(0, params.clone())
};
// try to add module, exporting non-existant env' variable => error
{
assert!(module_constructor(UserDefinedElements {
executor: None,
globals: HashMap::new(),
functions: ::std::borrow::Cow::from(NO_SIGNATURES),
}).is_err());
}
// now add simple variable natively => ok
{
assert_eq!(module_constructor(UserDefinedElements {
executor: None,
globals: vec![("ext_global".into(), Arc::new(VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(777)).unwrap()))].into_iter().collect(),
functions: ::std::borrow::Cow::from(NO_SIGNATURES),
}).unwrap().unwrap(), RuntimeValue::I32(777));
}
// now add 'getter+setter' variable natively => ok
{
assert_eq!(module_constructor(UserDefinedElements {
executor: None,
globals: vec![("ext_global".into(), Arc::new(VariableInstance::new_external_global(false, VariableType::I32, Box::new(MeasuredVariable { val: 345 })).unwrap()))].into_iter().collect(),
functions: ::std::borrow::Cow::from(NO_SIGNATURES),
}).unwrap().unwrap(), RuntimeValue::I32(345));
}
}
#[test]
fn native_custom_error() {
let program = program_with_default_env();
let env_instance = program.module("env").unwrap();
let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap();
let mut executor = FunctionExecutor { memory: env_memory.clone(), values: Vec::new() };
let functions = UserDefinedElements {
executor: Some(&mut executor),
globals: HashMap::new(),
functions: ::std::borrow::Cow::from(SIGNATURES),
};
let native_env_instance = native_module(env_instance, functions).unwrap();
let params = ExecutionParams::with_external("env".into(), native_env_instance);
let module = module()
.with_import(ImportEntry::new("env".into(), "err".into(), External::Function(0)))
.function()
.signature().param().i32().param().i32().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::GetLocal(0),
Opcode::GetLocal(1),
Opcode::Call(0),
Opcode::End,
])).build()
.build()
.build();
let module_instance = program.add_module("main", module, Some(&params.externals)).unwrap();
let user_error1 = match module_instance.execute_index(
0,
params
.clone()
.add_argument(RuntimeValue::I32(7))
.add_argument(RuntimeValue::I32(0)),
) {
Err(Error::User(user_error)) => user_error,
result => panic!("Unexpected result {:?}", result),
};
assert_eq!(user_error1.downcast_ref::<UserErrorWithCode>().unwrap(), &UserErrorWithCode { error_code: 777 });
let user_error2 = match module_instance.execute_index(
0,
params
.clone()
.add_argument(RuntimeValue::I32(7))
.add_argument(RuntimeValue::I32(0)),
) {
Err(Error::User(user_error)) => user_error,
result => panic!("Unexpected result {:?}", result),
};
assert_eq!(user_error2.downcast_ref::<UserErrorWithCode>().unwrap(), &UserErrorWithCode { error_code: 777 });
}
#[test]
fn env_native_export_entry_type_check() {
let program = program_with_default_env();
let env_instance = program.module("env").unwrap();
let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap();
let mut function_executor = FunctionExecutor {
memory: env_memory,
values: Vec::new(),
};
let native_env_instance = native_module(env_instance, UserDefinedElements {
executor: Some(&mut function_executor),
globals: vec![("ext_global".into(), Arc::new(VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(1312)).unwrap()))].into_iter().collect(),
functions: ::std::borrow::Cow::from(SIGNATURES),
}).unwrap();
assert!(native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I32))))).is_ok());
match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![], Some(ValueType::I32))))) {
Err(Error::Validation(_)) => { },
result => panic!("Unexpected result {:?}", result),
}
match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], None)))) {
Err(Error::Validation(_)) => { },
result => panic!("Unexpected result {:?}", result),
}
match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I64))))) {
Err(Error::Validation(_)) => { },
result => panic!("Unexpected result {:?}", result),
}
assert!(native_env_instance.export_entry("ext_global", &ExportEntryType::Global(VariableType::I32)).is_ok());
match native_env_instance.export_entry("ext_global", &ExportEntryType::Global(VariableType::F32)) {
Err(Error::Validation(_)) => { },
result => panic!("Unexpected result {:?}", result),
}
}
#[test]
fn memory_import_limits_initial() {
let core_module = module()
.memory().with_min(10).build()
.with_export(ExportEntry::new("memory".into(), Internal::Memory(0)))
.build();
let program = ProgramInstance::new();
program.add_module("core", core_module, None).unwrap();
let test_cases = vec![
(9, false),
(10, false),
(11, true),
];
for test_case in test_cases {
let (import_initial, is_error) = test_case;
let client_module = module()
.with_import(ImportEntry::new("core".into(), "memory".into(), External::Memory(MemoryType::new(import_initial, None))))
.build();
match program.add_module("client", client_module, None).map(|_| ()) {
Ok(_) if !is_error => (),
Err(Error::Validation(ref actual_error))
if is_error && actual_error == &format!("trying to import memory with initial=10 and import.initial={}", import_initial) => (),
x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x),
}
}
}
#[test]
fn memory_import_limits_maximum() {
#[derive(Debug, Clone, Copy, PartialEq)]
enum MaximumError { ValueMismatch, Ok };
let test_cases = vec![
(None, Some(100), MaximumError::Ok),
(Some(100), None, MaximumError::Ok),
(Some(100), Some(98), MaximumError::ValueMismatch),
(Some(100), Some(100), MaximumError::Ok),
(Some(100), Some(101), MaximumError::Ok),
(None, None, MaximumError::Ok),
];
let program = ProgramInstance::new();
for test_case in test_cases {
let (core_maximum, client_maximum, expected_err) = test_case;
let core_module = module()
.memory().with_min(10).with_max(core_maximum).build()
.with_export(ExportEntry::new("memory".into(), Internal::Memory(0)))
.build();
let client_module = module()
.with_import(ImportEntry::new("core".into(), "memory".into(), External::Memory(MemoryType::new(10, client_maximum))))
.build();
program.add_module("core", core_module, None).unwrap();
match program.add_module("client", client_module, None).map(|_| ()) {
Err(Error::Validation(actual_err)) => match expected_err {
MaximumError::ValueMismatch
if actual_err == format!("trying to import memory with maximum={} and import.maximum={}", core_maximum.unwrap_or_default(), client_maximum.unwrap_or_default()) => (),
_ => panic!("unexpected validation error for test_case {:?}: {}", test_case, actual_err),
},
Ok(_) if expected_err == MaximumError::Ok => (),
x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x),
}
}
}
#[test]
fn table_import_limits_initial() {
let core_module = module()
.table().with_min(10).build()
.with_export(ExportEntry::new("table".into(), Internal::Table(0)))
.build();
let program = ProgramInstance::new();
program.add_module("core", core_module, None).unwrap();
let test_cases = vec![
(9, false),
(10, false),
(11, true),
];
for test_case in test_cases {
let (import_initial, is_error) = test_case;
let client_module = module()
.with_import(ImportEntry::new("core".into(), "table".into(), External::Table(TableType::new(import_initial, None))))
.build();
match program.add_module("client", client_module, None).map(|_| ()) {
Ok(_) if !is_error => (),
Err(Error::Validation(ref actual_error))
if is_error && actual_error == &format!("trying to import table with initial=10 and import.initial={}", import_initial) => (),
x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x),
}
}
}
#[test]
fn table_import_limits_maximum() {
#[derive(Debug, Clone, Copy, PartialEq)]
enum MaximumError { ValueMismatch, Ok };
let test_cases = vec![
(None, Some(100), MaximumError::Ok),
(Some(100), None, MaximumError::Ok),
(Some(100), Some(98), MaximumError::ValueMismatch),
(Some(100), Some(100), MaximumError::Ok),
(Some(100), Some(101), MaximumError::Ok),
(None, None, MaximumError::Ok),
];
let program = ProgramInstance::new();
for test_case in test_cases {
let (core_maximum, client_maximum, expected_err) = test_case;
let core_module = module()
.table().with_min(10).with_max(core_maximum).build()
.with_export(ExportEntry::new("table".into(), Internal::Table(0)))
.build();
let client_module = module()
.with_import(ImportEntry::new("core".into(), "table".into(), External::Table(TableType::new(10, client_maximum))))
.build();
program.add_module("core", core_module, None).unwrap();
match program.add_module("client", client_module, None).map(|_| ()) {
Err(Error::Validation(actual_err)) => match expected_err {
MaximumError::ValueMismatch
if actual_err == format!("trying to import table with maximum={} and import.maximum={}", core_maximum.unwrap_or_default(), client_maximum.unwrap_or_default()) => (),
_ => panic!("unexpected validation error for test_case {:?}: {}", test_case, actual_err),
},
Ok(_) if expected_err == MaximumError::Ok => (),
x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x),
}
}
}

View File

@ -1,29 +0,0 @@
mod basics;
mod wabt;
mod wasm;
mod utils {
use elements::{Internal, ExportEntry, InitExpr, Opcode, ValueType, GlobalType, GlobalEntry};
use interpreter::ProgramInstance;
use builder::module;
pub fn program_with_default_env() -> ProgramInstance {
let program = ProgramInstance::new();
let env_module = module()
.memory()
.with_min(256) // 256 pages. 256 * 64K = 16MB
.build()
.with_export(ExportEntry::new("memory".into(), Internal::Memory(0)))
.table()
.with_min(64)
.build()
.with_export(ExportEntry::new("table".into(), Internal::Table(0)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0), Opcode::End])))
.with_export(ExportEntry::new("tableBase".into(), Internal::Global(0)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0), Opcode::End])))
.with_export(ExportEntry::new("memoryBase".into(), Internal::Global(1)))
.build();
program.add_module("env", env_module, None).unwrap();
program
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,77 +0,0 @@
use elements::deserialize_file;
use elements::Module;
use interpreter::ExecutionParams;
use interpreter::value::RuntimeValue;
use interpreter::module::{ModuleInstanceInterface, ItemIndex};
use super::utils::program_with_default_env;
#[test]
fn interpreter_inc_i32() {
// Name of function contained in WASM file (note the leading underline)
const FUNCTION_NAME: &'static str = "_inc_i32";
// The WASM file containing the module and function
const WASM_FILE: &str = &"res/cases/v1/inc_i32.wasm";
let program = program_with_default_env();
let module: Module =
deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer");
let i32_val = 42;
// the functions expects a single i32 parameter
let args = vec![RuntimeValue::I32(i32_val)];
let exp_retval = Some(RuntimeValue::I32(i32_val + 1));
let execution_params = ExecutionParams::from(args);
let module_result = program
.add_module("main", module, None);
let module = module_result
.expect("Failed to initialize module");
let retval = module
.execute_export(FUNCTION_NAME, execution_params)
.expect("");
assert_eq!(exp_retval, retval);
}
#[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/cases/v1/accumulate_u8.wasm";
// The octet sequence being accumulated
const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1];
let program = program_with_default_env();
// Load the module-structure from wasm-file and add to program
let module: Module =
deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer");
let module = program
.add_module("main", module, None)
.expect("Failed to initialize module");
// => env module is created
let env_instance = program.module("env").unwrap();
// => linear memory is created
let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap();
// 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 = vec![RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)];
let execution_params = ExecutionParams::from(args);
let retval = module
.execute_export(FUNCTION_NAME, execution_params)
.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));
// Verify calculation from WebAssembly runtime is identical to expected result
assert_eq!(exp_retval, retval);
}

View File

@ -1,809 +0,0 @@
use std::u32;
use std::sync::Arc;
use std::collections::HashMap;
use elements::{Opcode, BlockType, ValueType};
use interpreter::Error;
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, FunctionSignature};
use common::stack::StackWithLimit;
use interpreter::variable::VariableType;
/// Constant from wabt' validator.cc to skip alignment validation (not a part of spec).
const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
/// Function validation context.
pub struct FunctionValidationContext<'a> {
/// Wasm module instance (in process of instantiation).
module_instance: &'a ModuleInstance,
/// Native externals.
externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>,
/// Current instruction position.
position: usize,
/// Local variables.
locals: &'a [ValueType],
/// Value stack.
value_stack: StackWithLimit<StackValueType>,
/// Frame stack.
frame_stack: StackWithLimit<BlockFrame>,
/// Function return type. None if validating expression.
return_type: Option<BlockType>,
/// Labels positions.
labels: HashMap<usize, usize>,
}
/// Value type on the stack.
#[derive(Debug, Clone, Copy)]
pub enum StackValueType {
/// Any value type.
Any,
/// Any number of any values of any type.
AnyUnlimited,
/// Concrete value type.
Specific(ValueType),
}
/// Control stack frame.
#[derive(Debug, Clone)]
pub struct BlockFrame {
/// Frame type.
pub frame_type: BlockFrameType,
/// A signature, which is a block signature type indicating the number and types of result values of the region.
pub block_type: BlockType,
/// A label for reference to block instruction.
pub begin_position: usize,
/// A label for reference from branch instructions.
pub branch_position: usize,
/// A label for reference from end instructions.
pub end_position: usize,
/// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label.
pub value_stack_len: usize,
}
/// Type of block frame.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BlockFrameType {
/// Function frame.
Function,
/// Usual block frame.
Block,
/// Loop frame (branching to the beginning of block).
Loop,
/// True-subblock of if expression.
IfTrue,
/// False-subblock of if expression.
IfFalse,
}
/// Function validator.
pub struct Validator;
/// Instruction outcome.
#[derive(Debug, Clone)]
pub enum InstructionOutcome {
/// Continue with next instruction.
ValidateNextInstruction,
/// Unreachable instruction reached.
Unreachable,
}
impl Validator {
pub fn validate_function(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result<(), Error> {
context.push_label(BlockFrameType::Function, block_type)?;
Validator::validate_function_block(context, body)?;
while !context.frame_stack.is_empty() {
context.pop_label()?;
}
Ok(())
}
fn validate_function_block(context: &mut FunctionValidationContext, body: &[Opcode]) -> Result<(), Error> {
let body_len = body.len();
if body_len == 0 {
return Err(Error::Validation("Non-empty function body expected".into()));
}
loop {
let opcode = &body[context.position];
match Validator::validate_instruction(context, opcode)? {
InstructionOutcome::ValidateNextInstruction => (),
InstructionOutcome::Unreachable => context.unreachable()?,
}
context.position += 1;
if context.position >= body_len {
return Ok(());
}
}
}
fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result<InstructionOutcome, Error> {
debug!(target: "validator", "validating {:?}", opcode);
match opcode {
&Opcode::Unreachable => Ok(InstructionOutcome::Unreachable),
&Opcode::Nop => Ok(InstructionOutcome::ValidateNextInstruction),
&Opcode::Block(block_type) => Validator::validate_block(context, block_type),
&Opcode::Loop(block_type) => Validator::validate_loop(context, block_type),
&Opcode::If(block_type) => Validator::validate_if(context, block_type),
&Opcode::Else => Validator::validate_else(context),
&Opcode::End => Validator::validate_end(context),
&Opcode::Br(idx) => Validator::validate_br(context, idx),
&Opcode::BrIf(idx) => Validator::validate_br_if(context, idx),
&Opcode::BrTable(ref table, default) => Validator::validate_br_table(context, table, default),
&Opcode::Return => Validator::validate_return(context),
&Opcode::Call(index) => Validator::validate_call(context, index),
&Opcode::CallIndirect(index, _reserved) => Validator::validate_call_indirect(context, index),
&Opcode::Drop => Validator::validate_drop(context),
&Opcode::Select => Validator::validate_select(context),
&Opcode::GetLocal(index) => Validator::validate_get_local(context, index),
&Opcode::SetLocal(index) => Validator::validate_set_local(context, index),
&Opcode::TeeLocal(index) => Validator::validate_tee_local(context, index),
&Opcode::GetGlobal(index) => Validator::validate_get_global(context, index),
&Opcode::SetGlobal(index) => Validator::validate_set_global(context, index),
&Opcode::I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32.into()),
&Opcode::I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64.into()),
&Opcode::F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32.into()),
&Opcode::F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64.into()),
&Opcode::I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()),
&Opcode::I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()),
&Opcode::I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()),
&Opcode::I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()),
&Opcode::I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()),
&Opcode::I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()),
&Opcode::I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()),
&Opcode::I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()),
&Opcode::I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()),
&Opcode::I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()),
&Opcode::I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32.into()),
&Opcode::I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64.into()),
&Opcode::F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32.into()),
&Opcode::F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64.into()),
&Opcode::I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32.into()),
&Opcode::I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32.into()),
&Opcode::I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64.into()),
&Opcode::I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64.into()),
&Opcode::I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64.into()),
&Opcode::CurrentMemory(_) => Validator::validate_current_memory(context),
&Opcode::GrowMemory(_) => Validator::validate_grow_memory(context),
&Opcode::I32Const(_) => Validator::validate_const(context, ValueType::I32.into()),
&Opcode::I64Const(_) => Validator::validate_const(context, ValueType::I64.into()),
&Opcode::F32Const(_) => Validator::validate_const(context, ValueType::F32.into()),
&Opcode::F64Const(_) => Validator::validate_const(context, ValueType::F64.into()),
&Opcode::I32Eqz => Validator::validate_testop(context, ValueType::I32.into()),
&Opcode::I32Eq => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32Ne => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32LtS => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32LtU => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32GtS => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32GtU => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32LeS => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32LeU => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32GeS => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I32GeU => Validator::validate_relop(context, ValueType::I32.into()),
&Opcode::I64Eqz => Validator::validate_testop(context, ValueType::I64.into()),
&Opcode::I64Eq => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64Ne => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64LtS => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64LtU => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64GtS => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64GtU => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64LeS => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64LeU => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64GeS => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::I64GeU => Validator::validate_relop(context, ValueType::I64.into()),
&Opcode::F32Eq => Validator::validate_relop(context, ValueType::F32.into()),
&Opcode::F32Ne => Validator::validate_relop(context, ValueType::F32.into()),
&Opcode::F32Lt => Validator::validate_relop(context, ValueType::F32.into()),
&Opcode::F32Gt => Validator::validate_relop(context, ValueType::F32.into()),
&Opcode::F32Le => Validator::validate_relop(context, ValueType::F32.into()),
&Opcode::F32Ge => Validator::validate_relop(context, ValueType::F32.into()),
&Opcode::F64Eq => Validator::validate_relop(context, ValueType::F64.into()),
&Opcode::F64Ne => Validator::validate_relop(context, ValueType::F64.into()),
&Opcode::F64Lt => Validator::validate_relop(context, ValueType::F64.into()),
&Opcode::F64Gt => Validator::validate_relop(context, ValueType::F64.into()),
&Opcode::F64Le => Validator::validate_relop(context, ValueType::F64.into()),
&Opcode::F64Ge => Validator::validate_relop(context, ValueType::F64.into()),
&Opcode::I32Clz => Validator::validate_unop(context, ValueType::I32.into()),
&Opcode::I32Ctz => Validator::validate_unop(context, ValueType::I32.into()),
&Opcode::I32Popcnt => Validator::validate_unop(context, ValueType::I32.into()),
&Opcode::I32Add => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32Sub => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32Mul => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32DivS => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32DivU => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32RemS => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32RemU => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32And => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32Or => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32Xor => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32Shl => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32ShrS => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32ShrU => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32Rotl => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I32Rotr => Validator::validate_binop(context, ValueType::I32.into()),
&Opcode::I64Clz => Validator::validate_unop(context, ValueType::I64.into()),
&Opcode::I64Ctz => Validator::validate_unop(context, ValueType::I64.into()),
&Opcode::I64Popcnt => Validator::validate_unop(context, ValueType::I64.into()),
&Opcode::I64Add => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64Sub => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64Mul => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64DivS => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64DivU => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64RemS => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64RemU => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64And => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64Or => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64Xor => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64Shl => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64ShrS => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64ShrU => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64Rotl => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::I64Rotr => Validator::validate_binop(context, ValueType::I64.into()),
&Opcode::F32Abs => Validator::validate_unop(context, ValueType::F32.into()),
&Opcode::F32Neg => Validator::validate_unop(context, ValueType::F32.into()),
&Opcode::F32Ceil => Validator::validate_unop(context, ValueType::F32.into()),
&Opcode::F32Floor => Validator::validate_unop(context, ValueType::F32.into()),
&Opcode::F32Trunc => Validator::validate_unop(context, ValueType::F32.into()),
&Opcode::F32Nearest => Validator::validate_unop(context, ValueType::F32.into()),
&Opcode::F32Sqrt => Validator::validate_unop(context, ValueType::F32.into()),
&Opcode::F32Add => Validator::validate_binop(context, ValueType::F32.into()),
&Opcode::F32Sub => Validator::validate_binop(context, ValueType::F32.into()),
&Opcode::F32Mul => Validator::validate_binop(context, ValueType::F32.into()),
&Opcode::F32Div => Validator::validate_binop(context, ValueType::F32.into()),
&Opcode::F32Min => Validator::validate_binop(context, ValueType::F32.into()),
&Opcode::F32Max => Validator::validate_binop(context, ValueType::F32.into()),
&Opcode::F32Copysign => Validator::validate_binop(context, ValueType::F32.into()),
&Opcode::F64Abs => Validator::validate_unop(context, ValueType::F64.into()),
&Opcode::F64Neg => Validator::validate_unop(context, ValueType::F64.into()),
&Opcode::F64Ceil => Validator::validate_unop(context, ValueType::F64.into()),
&Opcode::F64Floor => Validator::validate_unop(context, ValueType::F64.into()),
&Opcode::F64Trunc => Validator::validate_unop(context, ValueType::F64.into()),
&Opcode::F64Nearest => Validator::validate_unop(context, ValueType::F64.into()),
&Opcode::F64Sqrt => Validator::validate_unop(context, ValueType::F64.into()),
&Opcode::F64Add => Validator::validate_binop(context, ValueType::F64.into()),
&Opcode::F64Sub => Validator::validate_binop(context, ValueType::F64.into()),
&Opcode::F64Mul => Validator::validate_binop(context, ValueType::F64.into()),
&Opcode::F64Div => Validator::validate_binop(context, ValueType::F64.into()),
&Opcode::F64Min => Validator::validate_binop(context, ValueType::F64.into()),
&Opcode::F64Max => Validator::validate_binop(context, ValueType::F64.into()),
&Opcode::F64Copysign => Validator::validate_binop(context, ValueType::F64.into()),
&Opcode::I32WrapI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::I32.into()),
&Opcode::I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()),
&Opcode::I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()),
&Opcode::I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()),
&Opcode::I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()),
&Opcode::I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()),
&Opcode::I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()),
&Opcode::I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()),
&Opcode::I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()),
&Opcode::I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()),
&Opcode::I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()),
&Opcode::F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()),
&Opcode::F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()),
&Opcode::F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()),
&Opcode::F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()),
&Opcode::F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::F32.into()),
&Opcode::F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()),
&Opcode::F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()),
&Opcode::F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()),
&Opcode::F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()),
&Opcode::F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::F64.into()),
&Opcode::I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()),
&Opcode::I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()),
&Opcode::F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()),
&Opcode::F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()),
}
}
fn validate_const(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.push_value(value_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_unop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type)?;
context.push_value(value_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_binop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type)?;
context.pop_value(value_type)?;
context.push_value(value_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_testop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type)?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_relop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type)?;
context.pop_value(value_type)?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: StackValueType, value_type2: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type1)?;
context.push_value(value_type2)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_drop(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.pop_any_value().map(|_| ())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_select(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
let select_type = context.pop_any_value()?;
context.pop_value(select_type)?;
context.push_value(select_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
context.push_value(local_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
let value_type = context.pop_any_value()?;
if local_type != value_type {
return Err(Error::Validation(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
let value_type = context.tee_any_value()?;
if local_type != value_type {
return Err(Error::Validation(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let global_type = context.require_global(index, None)?;
context.push_value(global_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let global_type = context.require_global(index, Some(true))?;
let value_type = context.pop_any_value()?;
if global_type != value_type {
return Err(Error::Validation(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type)));
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
if align != NATURAL_ALIGNMENT {
if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align {
return Err(Error::Validation(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align)));
}
}
context.pop_value(ValueType::I32.into())?;
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.push_value(value_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
if align != NATURAL_ALIGNMENT {
if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align {
return Err(Error::Validation(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align)));
}
}
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.pop_value(value_type)?;
context.pop_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_block(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.push_label(BlockFrameType::Block, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_loop(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.push_label(BlockFrameType::Loop, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
context.push_label(BlockFrameType::IfTrue, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_else(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
let block_type = {
let top_frame = context.top_label()?;
if top_frame.frame_type != BlockFrameType::IfTrue {
return Err(Error::Validation("Misplaced else instruction".into()));
}
top_frame.block_type
};
context.pop_label()?;
if let BlockType::Value(value_type) = block_type {
context.pop_value(value_type.into())?;
}
context.push_label(BlockFrameType::IfFalse, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_end(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
{
let top_frame = context.top_label()?;
if top_frame.frame_type == BlockFrameType::IfTrue {
if top_frame.block_type != BlockType::NoResult {
return Err(Error::Validation(format!("If block without else required to have NoResult block type. But it have {:?} type", top_frame.block_type)));
}
}
}
context.pop_label().map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
let (frame_type, frame_block_type) = {
let frame = context.require_label(idx)?;
(frame.frame_type, frame.block_type)
};
if frame_type != BlockFrameType::Loop {
if let BlockType::Value(value_type) = frame_block_type {
context.tee_value(value_type.into())?;
}
}
Ok(InstructionOutcome::Unreachable)
}
fn validate_br_if(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
let (frame_type, frame_block_type) = {
let frame = context.require_label(idx)?;
(frame.frame_type, frame.block_type)
};
if frame_type != BlockFrameType::Loop {
if let BlockType::Value(value_type) = frame_block_type {
context.tee_value(value_type.into())?;
}
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_br_table(context: &mut FunctionValidationContext, table: &[u32], default: u32) -> Result<InstructionOutcome, Error> {
let mut required_block_type = None;
{
let default_block = context.require_label(default)?;
if default_block.frame_type != BlockFrameType::Loop {
required_block_type = Some(default_block.block_type);
}
for label in table {
let label_block = context.require_label(*label)?;
if label_block.frame_type != BlockFrameType::Loop {
if let Some(required_block_type) = required_block_type {
if required_block_type != label_block.block_type {
return Err(Error::Validation(format!("Labels in br_table points to block of different types: {:?} and {:?}", required_block_type, label_block.block_type)));
}
}
required_block_type = Some(label_block.block_type);
}
}
}
context.pop_value(ValueType::I32.into())?;
if let Some(required_block_type) = required_block_type {
if let BlockType::Value(value_type) = required_block_type {
context.tee_value(value_type.into())?;
}
}
Ok(InstructionOutcome::Unreachable)
}
fn validate_return(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
if let BlockType::Value(value_type) = context.return_type()? {
context.tee_value(value_type.into())?;
}
Ok(InstructionOutcome::Unreachable)
}
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
let (argument_types, return_type) = context.require_function(idx)?;
for argument_type in argument_types.iter().rev() {
context.pop_value((*argument_type).into())?;
}
if let BlockType::Value(value_type) = return_type {
context.push_value(value_type.into())?;
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
context.require_table(DEFAULT_TABLE_INDEX, VariableType::AnyFunc)?;
context.pop_value(ValueType::I32.into())?;
let (argument_types, return_type) = context.require_function_type(idx)?;
for argument_type in argument_types.iter().rev() {
context.pop_value((*argument_type).into())?;
}
if let BlockType::Value(value_type) = return_type {
context.push_value(value_type.into())?;
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.pop_value(ValueType::I32.into())?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
}
impl<'a> FunctionValidationContext<'a> {
pub fn new(
module_instance: &'a ModuleInstance,
externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>,
locals: &'a [ValueType],
value_stack_limit: usize,
frame_stack_limit: usize,
function: FunctionSignature,
) -> Self {
FunctionValidationContext {
module_instance: module_instance,
externals: externals,
position: 0,
locals: locals,
value_stack: StackWithLimit::with_limit(value_stack_limit),
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
return_type: Some(function.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)),
labels: HashMap::new(),
}
}
pub fn push_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
Ok(self.value_stack.push(value_type.into())?)
}
pub fn pop_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
self.check_stack_access()?;
match self.value_stack.pop()? {
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()),
StackValueType::Any => Ok(()),
StackValueType::AnyUnlimited => {
self.value_stack.push(StackValueType::AnyUnlimited)?;
Ok(())
},
stack_value_type @ _ => Err(Error::Validation(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))),
}
}
pub fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
self.check_stack_access()?;
match *self.value_stack.top()? {
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()),
StackValueType::Any | StackValueType::AnyUnlimited => Ok(()),
stack_value_type @ _ => Err(Error::Validation(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))),
}
}
pub fn pop_any_value(&mut self) -> Result<StackValueType, Error> {
self.check_stack_access()?;
match self.value_stack.pop()? {
StackValueType::Specific(stack_value_type) => Ok(StackValueType::Specific(stack_value_type)),
StackValueType::Any => Ok(StackValueType::Any),
StackValueType::AnyUnlimited => {
self.value_stack.push(StackValueType::AnyUnlimited)?;
Ok(StackValueType::Any)
},
}
}
pub fn tee_any_value(&mut self) -> Result<StackValueType, Error> {
self.check_stack_access()?;
Ok(self.value_stack.top().map(Clone::clone)?)
}
pub fn unreachable(&mut self) -> Result<(), Error> {
Ok(self.value_stack.push(StackValueType::AnyUnlimited)?)
}
pub fn top_label(&self) -> Result<&BlockFrame, Error> {
Ok(self.frame_stack.top()?)
}
pub fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> {
Ok(self.frame_stack.push(BlockFrame {
frame_type: frame_type,
block_type: block_type,
begin_position: self.position,
branch_position: self.position,
end_position: self.position,
value_stack_len: self.value_stack.len(),
})?)
}
pub fn pop_label(&mut self) -> Result<InstructionOutcome, Error> {
let frame = self.frame_stack.pop()?;
let actual_value_type = if self.value_stack.len() > frame.value_stack_len {
Some(self.value_stack.pop()?)
} else {
None
};
self.value_stack.resize(frame.value_stack_len, StackValueType::Any);
match frame.block_type {
BlockType::NoResult if actual_value_type.map(|vt| vt.is_any_unlimited()).unwrap_or(true) => (),
BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) => (),
_ => return Err(Error::Validation(format!("Expected block to return {:?} while it has returned {:?}", frame.block_type, actual_value_type))),
}
if !self.frame_stack.is_empty() {
self.labels.insert(frame.begin_position, self.position);
}
if let BlockType::Value(value_type) = frame.block_type {
self.push_value(value_type.into())?;
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
pub fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> {
Ok(self.frame_stack.get(idx as usize)?)
}
pub fn return_type(&self) -> Result<BlockType, Error> {
self.return_type.ok_or(Error::Validation("Trying to return from expression".into()))
}
pub fn require_local(&self, idx: u32) -> Result<StackValueType, Error> {
self.locals.get(idx as usize)
.cloned()
.map(Into::into)
.ok_or(Error::Validation(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len())))
}
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<StackValueType, Error> {
self.module_instance
.global(ItemIndex::IndexSpace(idx), None, self.externals.clone())
.and_then(|g| match mutability {
Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be mutable", idx))),
Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be immutable", idx))),
_ => match g.variable_type() {
VariableType::AnyFunc => Err(Error::Validation(format!("Expected global {} to have non-AnyFunc type", idx))),
VariableType::I32 => Ok(StackValueType::Specific(ValueType::I32)),
VariableType::I64 => Ok(StackValueType::Specific(ValueType::I64)),
VariableType::F32 => Ok(StackValueType::Specific(ValueType::F32)),
VariableType::F64 => Ok(StackValueType::Specific(ValueType::F64)),
}
})
}
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
self.module_instance
.memory(ItemIndex::IndexSpace(idx))
.map(|_| ())
}
pub fn require_table(&self, idx: u32, variable_type: VariableType) -> Result<(), Error> {
self.module_instance
.table(ItemIndex::IndexSpace(idx))
.and_then(|t| if t.variable_type() == variable_type {
Ok(())
} else {
Err(Error::Validation(format!("Table {} has element type {:?} while {:?} expected", idx, t.variable_type(), variable_type)))
})
}
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
self.module_instance.function_type(ItemIndex::IndexSpace(idx))
.map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)))
}
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
self.module_instance.function_type_by_index(idx)
.map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)))
}
pub fn function_labels(self) -> HashMap<usize, usize> {
self.labels
}
fn check_stack_access(&self) -> Result<(), Error> {
let value_stack_min = self.frame_stack.top().expect("at least 1 topmost block").value_stack_len;
if self.value_stack.len() > value_stack_min {
Ok(())
} else {
Err(Error::Validation("Trying to access parent frame stack values.".into()))
}
}
}
impl StackValueType {
pub fn is_any(&self) -> bool {
match self {
&StackValueType::Any => true,
_ => false,
}
}
pub fn is_any_unlimited(&self) -> bool {
match self {
&StackValueType::AnyUnlimited => true,
_ => false,
}
}
pub fn value_type(&self) -> ValueType {
match self {
&StackValueType::Any | &StackValueType::AnyUnlimited => unreachable!("must be checked by caller"),
&StackValueType::Specific(value_type) => value_type,
}
}
}
impl From<ValueType> for StackValueType {
fn from(value_type: ValueType) -> Self {
StackValueType::Specific(value_type)
}
}
impl PartialEq<StackValueType> for StackValueType {
fn eq(&self, other: &StackValueType) -> bool {
if self.is_any() || other.is_any() || self.is_any_unlimited() || other.is_any_unlimited() {
true
} else {
self.value_type() == other.value_type()
}
}
}
impl PartialEq<ValueType> for StackValueType {
fn eq(&self, other: &ValueType) -> bool {
if self.is_any() || self.is_any_unlimited() {
true
} else {
self.value_type() == *other
}
}
}
impl PartialEq<StackValueType> for ValueType {
fn eq(&self, other: &StackValueType) -> bool {
other == self
}
}

View File

@ -1,664 +0,0 @@
use std::{i32, i64, u32, u64, f32};
use std::io;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use interpreter::Error;
use interpreter::variable::VariableType;
/// Runtime value.
#[derive(Debug, Clone, PartialEq)]
pub enum RuntimeValue {
/// Null value.
Null,
/// Reference to the function in the given module' function index space.
AnyFunc(String, u32),
/// 32b-length signed/unsigned int.
I32(i32),
/// 64b-length signed/unsigned int.
I64(i64),
/// 32b-length float.
F32(f32),
/// 64b-length float.
F64(f64),
}
/// Try to convert into trait.
pub trait TryInto<T, E> {
/// Try to convert self into other value.
fn try_into(self) -> Result<T, E>;
}
/// Convert one type to another by wrapping.
pub trait WrapInto<T> {
/// Convert one type to another by wrapping.
fn wrap_into(self) -> T;
}
/// Convert one type to another by rounding to the nearest integer towards zero.
pub trait TryTruncateInto<T, E> {
/// Convert one type to another by rounding to the nearest integer towards zero.
fn try_truncate_into(self) -> Result<T, E>;
}
/// Convert one type to another by extending with leading zeroes.
pub trait ExtendInto<T> {
/// Convert one type to another by extending with leading zeroes.
fn extend_into(self) -> T;
}
/// Reinterprets the bits of a value of one type as another type.
pub trait TransmuteInto<T> {
/// Reinterprets the bits of a value of one type as another type.
fn transmute_into(self) -> T;
}
/// Convert from and to little endian.
pub trait LittleEndianConvert where Self: Sized {
/// Convert to little endian buffer.
fn into_little_endian(self) -> Vec<u8>;
/// Convert from little endian buffer.
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error>;
}
/// Arithmetic operations.
pub trait ArithmeticOps<T> {
/// Add two values.
fn add(self, other: T) -> T;
/// Subtract two values.
fn sub(self, other: T) -> T;
/// Multiply two values.
fn mul(self, other: T) -> T;
/// Divide two values.
fn div(self, other: T) -> Result<T, Error>;
}
/// Integer value.
pub trait Integer<T>: ArithmeticOps<T> {
/// Counts leading zeros in the bitwise representation of the value.
fn leading_zeros(self) -> T;
/// Counts trailing zeros in the bitwise representation of the value.
fn trailing_zeros(self) -> T;
/// Counts 1-bits in the bitwise representation of the value.
fn count_ones(self) -> T;
/// Get left bit rotation result.
fn rotl(self, other: T) -> T;
/// Get right bit rotation result.
fn rotr(self, other: T) -> T;
/// Get division remainder.
fn rem(self, other: T) -> Result<T, Error>;
}
/// Float-point value.
pub trait Float<T>: ArithmeticOps<T> {
/// Get absolute value.
fn abs(self) -> T;
/// Returns the largest integer less than or equal to a number.
fn floor(self) -> T;
/// Returns the smallest integer greater than or equal to a number.
fn ceil(self) -> T;
/// Returns the integer part of a number.
fn trunc(self) -> T;
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
fn round(self) -> T;
/// Returns the nearest integer to a number. Ties are round to even number.
fn nearest(self) -> T;
/// Takes the square root of a number.
fn sqrt(self) -> T;
/// Returns the minimum of the two numbers.
fn min(self, other: T) -> T;
/// Returns the maximum of the two numbers.
fn max(self, other: T) -> T;
/// Sets sign of this value to the sign of other value.
fn copysign(self, other: T) -> T;
}
impl RuntimeValue {
/// Creates new default value of given type.
pub fn default(variable_type: VariableType) -> Self {
match variable_type {
VariableType::AnyFunc => RuntimeValue::AnyFunc("".into(), 0),
VariableType::I32 => RuntimeValue::I32(0),
VariableType::I64 => RuntimeValue::I64(0),
VariableType::F32 => RuntimeValue::F32(0f32),
VariableType::F64 => RuntimeValue::F64(0f64),
}
}
/// Creates new value by interpreting passed u32 as f32.
pub fn decode_f32(val: u32) -> Self {
RuntimeValue::F32(f32_from_bits(val))
}
/// Creates new value by interpreting passed u64 as f64.
pub fn decode_f64(val: u64) -> Self {
RuntimeValue::F64(f64_from_bits(val))
}
/// Returns true if value is null.
pub fn is_null(&self) -> bool {
match *self {
RuntimeValue::Null => true,
_ => false,
}
}
/// Get variable type for this value.
pub fn variable_type(&self) -> Option<VariableType> {
match *self {
RuntimeValue::Null => None,
RuntimeValue::AnyFunc(_, _) => Some(VariableType::AnyFunc),
RuntimeValue::I32(_) => Some(VariableType::I32),
RuntimeValue::I64(_) => Some(VariableType::I64),
RuntimeValue::F32(_) => Some(VariableType::F32),
RuntimeValue::F64(_) => Some(VariableType::F64),
}
}
}
impl From<i32> for RuntimeValue {
fn from(val: i32) -> Self {
RuntimeValue::I32(val)
}
}
impl From<i64> for RuntimeValue {
fn from(val: i64) -> Self {
RuntimeValue::I64(val)
}
}
impl From<f32> for RuntimeValue {
fn from(val: f32) -> Self {
RuntimeValue::F32(val)
}
}
impl From<f64> for RuntimeValue {
fn from(val: f64) -> Self {
RuntimeValue::F64(val)
}
}
impl TryInto<bool, Error> for RuntimeValue {
fn try_into(self) -> Result<bool, Error> {
match self {
RuntimeValue::I32(val) => Ok(val != 0),
_ => Err(Error::Value(format!("32-bit int value expected"))),
}
}
}
impl TryInto<i32, Error> for RuntimeValue {
fn try_into(self) -> Result<i32, Error> {
match self {
RuntimeValue::I32(val) => Ok(val),
_ => Err(Error::Value(format!("32-bit int value expected"))),
}
}
}
impl TryInto<i64, Error> for RuntimeValue {
fn try_into(self) -> Result<i64, Error> {
match self {
RuntimeValue::I64(val) => Ok(val),
_ => Err(Error::Value(format!("64-bit int value expected"))),
}
}
}
impl TryInto<f32, Error> for RuntimeValue {
fn try_into(self) -> Result<f32, Error> {
match self {
RuntimeValue::F32(val) => Ok(val),
_ => Err(Error::Value(format!("32-bit float value expected"))),
}
}
}
impl TryInto<f64, Error> for RuntimeValue {
fn try_into(self) -> Result<f64, Error> {
match self {
RuntimeValue::F64(val) => Ok(val),
_ => Err(Error::Value(format!("64-bit float value expected"))),
}
}
}
impl TryInto<u32, Error> for RuntimeValue {
fn try_into(self) -> Result<u32, Error> {
match self {
RuntimeValue::I32(val) => Ok(val as u32),
_ => Err(Error::Value(format!("32-bit int value expected"))),
}
}
}
impl TryInto<u64, Error> for RuntimeValue {
fn try_into(self) -> Result<u64, Error> {
match self {
RuntimeValue::I64(val) => Ok(val as u64),
_ => Err(Error::Value(format!("64-bit int value expected"))),
}
}
}
macro_rules! impl_wrap_into {
($from: ident, $into: ident) => {
impl WrapInto<$into> for $from {
fn wrap_into(self) -> $into {
self as $into
}
}
}
}
impl_wrap_into!(i32, i8);
impl_wrap_into!(i32, i16);
impl_wrap_into!(i64, i8);
impl_wrap_into!(i64, i16);
impl_wrap_into!(i64, i32);
impl_wrap_into!(i64, f32);
impl_wrap_into!(u64, f32);
// Casting from an f64 to an f32 will produce the closest possible value (rounding strategy unspecified)
// NOTE: currently this will cause Undefined Behavior if the value is finite but larger or smaller than the
// largest or smallest finite value representable by f32. This is a bug and will be fixed.
impl_wrap_into!(f64, f32);
macro_rules! impl_try_truncate_into {
($from: ident, $into: ident) => {
impl TryTruncateInto<$into, Error> for $from {
fn try_truncate_into(self) -> Result<$into, Error> {
// Casting from a float to an integer will round the float towards zero
// NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the
// target integer type. This includes Inf and NaN. This is a bug and will be fixed.
if self.is_nan() || self.is_infinite() {
return Err(Error::Value("invalid float value for this operation".into()));
}
// range check
let result = self as $into;
if result as $from != self.trunc() {
return Err(Error::Value("invalid float value for this operation".into()));
}
Ok(self as $into)
}
}
}
}
impl_try_truncate_into!(f32, i32);
impl_try_truncate_into!(f32, i64);
impl_try_truncate_into!(f64, i32);
impl_try_truncate_into!(f64, i64);
impl_try_truncate_into!(f32, u32);
impl_try_truncate_into!(f32, u64);
impl_try_truncate_into!(f64, u32);
impl_try_truncate_into!(f64, u64);
macro_rules! impl_extend_into {
($from: ident, $into: ident) => {
impl ExtendInto<$into> for $from {
fn extend_into(self) -> $into {
self as $into
}
}
}
}
impl_extend_into!(i8, i32);
impl_extend_into!(u8, i32);
impl_extend_into!(i16, i32);
impl_extend_into!(u16, i32);
impl_extend_into!(i8, i64);
impl_extend_into!(u8, i64);
impl_extend_into!(i16, i64);
impl_extend_into!(u16, i64);
impl_extend_into!(i32, i64);
impl_extend_into!(u32, i64);
impl_extend_into!(u32, u64);
impl_extend_into!(i32, f32);
impl_extend_into!(i32, f64);
impl_extend_into!(u32, f32);
impl_extend_into!(u32, f64);
impl_extend_into!(i64, f64);
impl_extend_into!(u64, f64);
impl_extend_into!(f32, f64);
macro_rules! impl_transmute_into_self {
($type: ident) => {
impl TransmuteInto<$type> for $type {
fn transmute_into(self) -> $type {
self
}
}
}
}
impl_transmute_into_self!(i32);
impl_transmute_into_self!(i64);
impl_transmute_into_self!(f32);
impl_transmute_into_self!(f64);
macro_rules! impl_transmute_into_as {
($from: ident, $into: ident) => {
impl TransmuteInto<$into> for $from {
fn transmute_into(self) -> $into {
self as $into
}
}
}
}
impl_transmute_into_as!(i8, u8);
impl_transmute_into_as!(u8, i8);
impl_transmute_into_as!(i32, u32);
impl_transmute_into_as!(u32, i32);
impl_transmute_into_as!(i64, u64);
impl_transmute_into_as!(u64, i64);
// TODO: rewrite these safely when `f32/f32::to_bits/from_bits` stabilized.
impl TransmuteInto<i32> for f32 {
fn transmute_into(self) -> i32 { unsafe { ::std::mem::transmute(self) } }
}
impl TransmuteInto<i64> for f64 {
fn transmute_into(self) -> i64 { unsafe { ::std::mem::transmute(self) } }
}
impl TransmuteInto<f32> for i32 {
fn transmute_into(self) -> f32 { f32_from_bits(self as _) }
}
impl TransmuteInto<f64> for i64 {
fn transmute_into(self) -> f64 { f64_from_bits(self as _) }
}
impl LittleEndianConvert for i8 {
fn into_little_endian(self) -> Vec<u8> {
vec![self as u8]
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
buffer.get(0)
.map(|v| *v as i8)
.ok_or(Error::Value("invalid little endian buffer".into()))
}
}
impl LittleEndianConvert for u8 {
fn into_little_endian(self) -> Vec<u8> {
vec![self]
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
buffer.get(0)
.cloned()
.ok_or(Error::Value("invalid little endian buffer".into()))
}
}
impl LittleEndianConvert for i16 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(2);
vec.write_i16::<LittleEndian>(self)
.expect("i16 is written without any errors");
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_i16::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
}
impl LittleEndianConvert for u16 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(2);
vec.write_u16::<LittleEndian>(self)
.expect("u16 is written without any errors");
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_u16::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
}
impl LittleEndianConvert for i32 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(4);
vec.write_i32::<LittleEndian>(self)
.expect("i32 is written without any errors");
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_i32::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
}
impl LittleEndianConvert for u32 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(4);
vec.write_u32::<LittleEndian>(self)
.expect("u32 is written without any errors");
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_u32::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
}
impl LittleEndianConvert for i64 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(8);
vec.write_i64::<LittleEndian>(self)
.expect("i64 is written without any errors");
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_i64::<LittleEndian>()
.map_err(|e| Error::Value(e.to_string()))
}
}
impl LittleEndianConvert for f32 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(4);
vec.write_f32::<LittleEndian>(self)
.expect("f32 is written without any errors");
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_u32::<LittleEndian>()
.map(f32_from_bits)
.map_err(|e| Error::Value(e.to_string()))
}
}
impl LittleEndianConvert for f64 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(8);
vec.write_f64::<LittleEndian>(self)
.expect("i64 is written without any errors");
vec
}
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_u64::<LittleEndian>()
.map(f64_from_bits)
.map_err(|e| Error::Value(e.to_string()))
}
}
// Convert u32 to f32 safely, masking out sNAN
fn f32_from_bits(mut v: u32) -> f32 {
const EXP_MASK: u32 = 0x7F800000;
const QNAN_MASK: u32 = 0x00400000;
const FRACT_MASK: u32 = 0x007FFFFF;
if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
// If we have a NaN value, we
// convert signaling NaN values to quiet NaN
// by setting the the highest bit of the fraction
// TODO: remove when https://github.com/BurntSushi/byteorder/issues/71 closed.
// or `f32::from_bits` stabilized.
v |= QNAN_MASK;
}
unsafe { ::std::mem::transmute(v) }
}
// Convert u64 to f64 safely, masking out sNAN
fn f64_from_bits(mut v: u64) -> f64 {
const EXP_MASK: u64 = 0x7FF0000000000000;
const QNAN_MASK: u64 = 0x0001000000000000;
const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF;
if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
// If we have a NaN value, we
// convert signaling NaN values to quiet NaN
// by setting the the highest bit of the fraction
// TODO: remove when https://github.com/BurntSushi/byteorder/issues/71 closed.
// or `f64::from_bits` stabilized.
v |= QNAN_MASK;
}
unsafe { ::std::mem::transmute(v) }
}
macro_rules! impl_integer_arithmetic_ops {
($type: ident) => {
impl ArithmeticOps<$type> for $type {
fn add(self, other: $type) -> $type { self.wrapping_add(other) }
fn sub(self, other: $type) -> $type { self.wrapping_sub(other) }
fn mul(self, other: $type) -> $type { self.wrapping_mul(other) }
fn div(self, other: $type) -> Result<$type, Error> {
if other == 0 { Err(Error::Value("Division by zero".to_owned())) }
else {
let (result, overflow) = self.overflowing_div(other);
if overflow {
return Err(Error::Value("Attempt to divide with overflow".to_owned()));
} else {
Ok(result)
}
}
}
}
}
}
impl_integer_arithmetic_ops!(i32);
impl_integer_arithmetic_ops!(u32);
impl_integer_arithmetic_ops!(i64);
impl_integer_arithmetic_ops!(u64);
macro_rules! impl_float_arithmetic_ops {
($type: ident) => {
impl ArithmeticOps<$type> for $type {
fn add(self, other: $type) -> $type { self + other }
fn sub(self, other: $type) -> $type { self - other }
fn mul(self, other: $type) -> $type { self * other }
fn div(self, other: $type) -> Result<$type, Error> { Ok(self / other) }
}
}
}
impl_float_arithmetic_ops!(f32);
impl_float_arithmetic_ops!(f64);
macro_rules! impl_integer {
($type: ident) => {
impl Integer<$type> for $type {
fn leading_zeros(self) -> $type { self.leading_zeros() as $type }
fn trailing_zeros(self) -> $type { self.trailing_zeros() as $type }
fn count_ones(self) -> $type { self.count_ones() as $type }
fn rotl(self, other: $type) -> $type { self.rotate_left(other as u32) }
fn rotr(self, other: $type) -> $type { self.rotate_right(other as u32) }
fn rem(self, other: $type) -> Result<$type, Error> {
if other == 0 { Err(Error::Value("Division by zero".to_owned())) }
else { Ok(self.wrapping_rem(other)) }
}
}
}
}
impl_integer!(i32);
impl_integer!(u32);
impl_integer!(i64);
impl_integer!(u64);
macro_rules! impl_float {
($type: ident, $int_type: ident) => {
impl Float<$type> for $type {
fn abs(self) -> $type { self.abs() }
fn floor(self) -> $type { self.floor() }
fn ceil(self) -> $type { self.ceil() }
fn trunc(self) -> $type { self.trunc() }
fn round(self) -> $type { self.round() }
fn nearest(self) -> $type {
let round = self.round();
if self.fract().abs() != 0.5 {
return round;
}
use std::ops::Rem;
if round.rem(2.0) == 1.0 {
self.floor()
} else if round.rem(2.0) == -1.0 {
self.ceil()
} else {
round
}
}
fn sqrt(self) -> $type { self.sqrt() }
// This instruction corresponds to what is sometimes called "minNaN" in other languages.
fn min(self, other: $type) -> $type {
if self.is_nan() || other.is_nan() {
use std::$type;
return $type::NAN;
}
self.min(other)
}
// This instruction corresponds to what is sometimes called "maxNaN" in other languages.
fn max(self, other: $type) -> $type {
if self.is_nan() || other.is_nan() {
use std::$type;
return $type::NAN;
}
self.max(other)
}
fn copysign(self, other: $type) -> $type {
use std::mem::size_of;
if self.is_nan() {
return self;
}
let sign_mask: $int_type = 1 << ((size_of::<$int_type>() << 3) - 1);
let self_int: $int_type = self.transmute_into();
let other_int: $int_type = other.transmute_into();
let is_self_sign_set = (self_int & sign_mask) != 0;
let is_other_sign_set = (other_int & sign_mask) != 0;
if is_self_sign_set == is_other_sign_set {
self
} else if is_other_sign_set {
(self_int | sign_mask).transmute_into()
} else {
(self_int & !sign_mask).transmute_into()
}
}
}
}
}
impl_float!(f32, i32);
impl_float!(f64, i64);

View File

@ -1,163 +0,0 @@
use std::fmt;
use parking_lot::RwLock;
use elements::{GlobalType, ValueType, TableElementType};
use interpreter::Error;
use interpreter::value::RuntimeValue;
/// Variable type.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum VariableType {
/// Any func value.
AnyFunc,
/// i32 value.
I32,
/// i64 value.
I64,
/// f32 value.
F32,
/// f64 value.
F64,
}
/// Externally stored variable value.
///
/// WebAssembly specificaiton [requires][0] that if a global variable is immutable, then
/// it should remain unchanged. To comply with specification you should ensure this invariant holds.
///
/// [0]: https://webassembly.github.io/spec/appendix/properties.html#global-instance
pub trait ExternalVariableValue {
/// Get variable value.
fn get(&self) -> RuntimeValue;
/// Set variable value.
fn set(&mut self, value: RuntimeValue) -> Result<(), Error>;
}
/// Variable instance.
#[derive(Debug)]
pub struct VariableInstance {
/// Is mutable?
is_mutable: bool,
/// Variable type.
variable_type: VariableType,
/// Global value.
value: RwLock<VariableValue>,
}
/// Enum variable value.
enum VariableValue {
/// Internal value.
Internal(RuntimeValue),
/// External value.
External(Box<ExternalVariableValue>),
}
impl VariableInstance {
/// New variable instance
pub fn new(is_mutable: bool, variable_type: VariableType, value: RuntimeValue) -> Result<Self, Error> {
// TODO: there is nothing about null value in specification + there is nothing about initializing missing table elements? => runtime check for nulls
if !value.is_null() && value.variable_type() != Some(variable_type) {
return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, value.variable_type())));
}
Ok(VariableInstance {
is_mutable: is_mutable,
variable_type: variable_type,
value: RwLock::new(VariableValue::Internal(value)),
})
}
/// New global variable
pub fn new_global(global_type: &GlobalType, value: RuntimeValue) -> Result<Self, Error> {
Self::new(global_type.is_mutable(), global_type.content_type().into(), value)
}
/// New global with externally stored value.
pub fn new_external_global(is_mutable: bool, variable_type: VariableType, value: Box<ExternalVariableValue>) -> Result<Self, Error> {
// TODO: there is nothing about null value in specification + there is nothing about initializing missing table elements? => runtime check for nulls
let current_value = value.get();
if !current_value.is_null() && current_value.variable_type() != Some(variable_type) {
return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, current_value.variable_type())));
}
Ok(VariableInstance {
is_mutable: is_mutable,
variable_type: variable_type,
value: RwLock::new(VariableValue::External(value)),
})
}
/// Is mutable
pub fn is_mutable(&self) -> bool {
self.is_mutable
}
/// Get variable type.
pub fn variable_type(&self) -> VariableType {
self.variable_type
}
/// Get the value of the variable instance
pub fn get(&self) -> RuntimeValue {
self.value.read().get()
}
/// Set the value of the variable instance
pub fn set(&self, value: RuntimeValue) -> Result<(), Error> {
if !self.is_mutable {
return Err(Error::Variable("trying to update immutable variable".into()));
}
if value.variable_type() != Some(self.variable_type) {
return Err(Error::Variable(format!("trying to update variable of type {:?} with value of type {:?}", self.variable_type, value.variable_type())));
}
self.value.write().set(value)
}
}
impl VariableValue {
fn get(&self) -> RuntimeValue {
match *self {
VariableValue::Internal(ref value) => value.clone(),
VariableValue::External(ref value) => value.get(),
}
}
fn set(&mut self, new_value: RuntimeValue) -> Result<(), Error> {
match *self {
VariableValue::Internal(ref mut value) => {
*value = new_value;
Ok(())
},
VariableValue::External(ref mut value) => value.set(new_value),
}
}
}
impl From<ValueType> for VariableType {
fn from(vt: ValueType) -> VariableType {
match vt {
ValueType::I32 => VariableType::I32,
ValueType::I64 => VariableType::I64,
ValueType::F32 => VariableType::F32,
ValueType::F64 => VariableType::F64,
}
}
}
impl From<TableElementType> for VariableType {
fn from(tt: TableElementType) -> VariableType {
match tt {
TableElementType::AnyFunc => VariableType::AnyFunc,
}
}
}
impl fmt::Debug for VariableValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
VariableValue::Internal(ref value) => write!(f, "Variable.Internal({:?})", value),
VariableValue::External(ref value) => write!(f, "Variable.External({:?})", value.get()),
}
}
}

View File

@ -2,16 +2,10 @@
#![warn(missing_docs)] #![warn(missing_docs)]
#[macro_use]
extern crate log;
extern crate byteorder; extern crate byteorder;
extern crate parking_lot;
pub mod elements; pub mod elements;
pub mod builder; pub mod builder;
pub mod interpreter;
mod validation;
mod common;
pub use elements::{ pub use elements::{
Error as SerializationError, Error as SerializationError,
@ -22,10 +16,3 @@ pub use elements::{
peek_size, peek_size,
}; };
#[allow(deprecated)]
pub use interpreter::{
ProgramInstance,
ModuleInstance,
ModuleInstanceInterface,
RuntimeValue,
};

View File

@ -1,82 +0,0 @@
use elements::{MemoryType, TableType, GlobalType, Type};
use elements::{BlockType, ValueType};
use validation::Error;
#[derive(Default, Debug)]
pub struct ModuleContext {
pub memories: Vec<MemoryType>,
pub tables: Vec<TableType>,
pub globals: Vec<GlobalType>,
pub types: Vec<Type>,
pub func_type_indexes: Vec<u32>,
}
impl ModuleContext {
pub fn memories(&self) -> &[MemoryType] {
&self.memories
}
pub fn tables(&self) -> &[TableType] {
&self.tables
}
pub fn globals(&self) -> &[GlobalType] {
&self.globals
}
pub fn types(&self) -> &[Type] {
&self.types
}
pub fn func_type_indexes(&self) -> &[u32] {
&self.func_type_indexes
}
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
if self.memories().get(idx as usize).is_none() {
return Err(Error(format!("Memory at index {} doesn't exists", idx)));
}
Ok(())
}
pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> {
self.tables()
.get(idx as usize)
.ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx)))
}
pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
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 &Type::Function(ref 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()
.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()
.get(idx as usize)
.ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?;
if let Some(expected_mutable) = mutability {
if expected_mutable && !global.is_mutable() {
return Err(Error(format!("Expected global {} to be mutable", idx)));
}
if !expected_mutable && global.is_mutable() {
return Err(Error(format!("Expected global {} to be immutable", idx)));
}
}
Ok(global)
}
}

View File

@ -1,774 +0,0 @@
use std::u32;
use std::iter::repeat;
use std::collections::HashMap;
use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody};
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use validation::context::ModuleContext;
use validation::Error;
use common::stack::StackWithLimit;
use common::{BlockFrame, BlockFrameType};
/// Constant from wabt' validator.cc to skip alignment validation (not a part of spec).
const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
/// Maximum number of entries in value stack.
const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
/// Maximum number of entries in frame stack.
const DEFAULT_FRAME_STACK_LIMIT: usize = 1024;
/// Function validation context.
struct FunctionValidationContext<'a> {
/// Wasm module
module: &'a ModuleContext,
/// Current instruction position.
position: usize,
/// Local variables.
locals: &'a [ValueType],
/// Value stack.
value_stack: StackWithLimit<StackValueType>,
/// Frame stack.
frame_stack: StackWithLimit<BlockFrame>,
/// Function return type. None if validating expression.
return_type: Option<BlockType>,
/// Labels positions.
labels: HashMap<usize, usize>,
}
/// Value type on the stack.
#[derive(Debug, Clone, Copy)]
enum StackValueType {
/// Any value type.
Any,
/// Any number of any values of any type.
AnyUnlimited,
/// Concrete value type.
Specific(ValueType),
}
/// Function validator.
pub struct Validator;
/// Instruction outcome.
#[derive(Debug, Clone)]
enum InstructionOutcome {
/// Continue with next instruction.
ValidateNextInstruction,
/// Unreachable instruction reached.
Unreachable,
}
impl Validator {
pub fn validate_function(
module: &ModuleContext,
func: &Func,
body: &FuncBody,
) -> Result<(), Error> {
let (params, result_ty) = module.require_function_type(func.type_ref())?;
// locals = (params + vars)
let mut locals = params.to_vec();
locals.extend(
body.locals()
.iter()
.flat_map(|l| repeat(l.value_type())
.take(l.count() as usize)
),
);
let mut context = FunctionValidationContext::new(
&module,
&locals,
DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_STACK_LIMIT,
result_ty,
);
context.push_label(BlockFrameType::Function, result_ty)?;
Validator::validate_function_block(&mut context, body.code().elements())?;
while !context.frame_stack.is_empty() {
context.pop_label()?;
}
Ok(())
}
fn validate_function_block(context: &mut FunctionValidationContext, body: &[Opcode]) -> Result<(), Error> {
let body_len = body.len();
if body_len == 0 {
return Err(Error("Non-empty function body expected".into()));
}
loop {
let opcode = &body[context.position];
match Validator::validate_instruction(context, opcode)? {
InstructionOutcome::ValidateNextInstruction => (),
InstructionOutcome::Unreachable => context.unreachable()?,
}
context.position += 1;
if context.position == body_len {
return Ok(());
}
}
}
fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result<InstructionOutcome, Error> {
use self::Opcode::*;
debug!(target: "validator", "validating {:?}", opcode);
match *opcode {
Unreachable => Ok(InstructionOutcome::Unreachable),
Nop => Ok(InstructionOutcome::ValidateNextInstruction),
Block(block_type) => Validator::validate_block(context, block_type),
Loop(block_type) => Validator::validate_loop(context, block_type),
If(block_type) => Validator::validate_if(context, block_type),
Else => Validator::validate_else(context),
End => Validator::validate_end(context),
Br(idx) => Validator::validate_br(context, idx),
BrIf(idx) => Validator::validate_br_if(context, idx),
BrTable(ref table, default) => Validator::validate_br_table(context, table, default),
Return => Validator::validate_return(context),
Call(index) => Validator::validate_call(context, index),
CallIndirect(index, _reserved) => Validator::validate_call_indirect(context, index),
Drop => Validator::validate_drop(context),
Select => Validator::validate_select(context),
GetLocal(index) => Validator::validate_get_local(context, index),
SetLocal(index) => Validator::validate_set_local(context, index),
TeeLocal(index) => Validator::validate_tee_local(context, index),
GetGlobal(index) => Validator::validate_get_global(context, index),
SetGlobal(index) => Validator::validate_set_global(context, index),
I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32),
I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64),
F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32),
F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64),
I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32),
I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32),
I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32),
I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32),
I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64),
I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64),
I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64),
I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64),
I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64),
I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64),
I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32),
I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64),
F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32),
F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64),
I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32),
I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32),
I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64),
I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64),
I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64),
CurrentMemory(_) => Validator::validate_current_memory(context),
GrowMemory(_) => Validator::validate_grow_memory(context),
I32Const(_) => Validator::validate_const(context, ValueType::I32),
I64Const(_) => Validator::validate_const(context, ValueType::I64),
F32Const(_) => Validator::validate_const(context, ValueType::F32),
F64Const(_) => Validator::validate_const(context, ValueType::F64),
I32Eqz => Validator::validate_testop(context, ValueType::I32),
I32Eq => Validator::validate_relop(context, ValueType::I32),
I32Ne => Validator::validate_relop(context, ValueType::I32),
I32LtS => Validator::validate_relop(context, ValueType::I32),
I32LtU => Validator::validate_relop(context, ValueType::I32),
I32GtS => Validator::validate_relop(context, ValueType::I32),
I32GtU => Validator::validate_relop(context, ValueType::I32),
I32LeS => Validator::validate_relop(context, ValueType::I32),
I32LeU => Validator::validate_relop(context, ValueType::I32),
I32GeS => Validator::validate_relop(context, ValueType::I32),
I32GeU => Validator::validate_relop(context, ValueType::I32),
I64Eqz => Validator::validate_testop(context, ValueType::I64),
I64Eq => Validator::validate_relop(context, ValueType::I64),
I64Ne => Validator::validate_relop(context, ValueType::I64),
I64LtS => Validator::validate_relop(context, ValueType::I64),
I64LtU => Validator::validate_relop(context, ValueType::I64),
I64GtS => Validator::validate_relop(context, ValueType::I64),
I64GtU => Validator::validate_relop(context, ValueType::I64),
I64LeS => Validator::validate_relop(context, ValueType::I64),
I64LeU => Validator::validate_relop(context, ValueType::I64),
I64GeS => Validator::validate_relop(context, ValueType::I64),
I64GeU => Validator::validate_relop(context, ValueType::I64),
F32Eq => Validator::validate_relop(context, ValueType::F32),
F32Ne => Validator::validate_relop(context, ValueType::F32),
F32Lt => Validator::validate_relop(context, ValueType::F32),
F32Gt => Validator::validate_relop(context, ValueType::F32),
F32Le => Validator::validate_relop(context, ValueType::F32),
F32Ge => Validator::validate_relop(context, ValueType::F32),
F64Eq => Validator::validate_relop(context, ValueType::F64),
F64Ne => Validator::validate_relop(context, ValueType::F64),
F64Lt => Validator::validate_relop(context, ValueType::F64),
F64Gt => Validator::validate_relop(context, ValueType::F64),
F64Le => Validator::validate_relop(context, ValueType::F64),
F64Ge => Validator::validate_relop(context, ValueType::F64),
I32Clz => Validator::validate_unop(context, ValueType::I32),
I32Ctz => Validator::validate_unop(context, ValueType::I32),
I32Popcnt => Validator::validate_unop(context, ValueType::I32),
I32Add => Validator::validate_binop(context, ValueType::I32),
I32Sub => Validator::validate_binop(context, ValueType::I32),
I32Mul => Validator::validate_binop(context, ValueType::I32),
I32DivS => Validator::validate_binop(context, ValueType::I32),
I32DivU => Validator::validate_binop(context, ValueType::I32),
I32RemS => Validator::validate_binop(context, ValueType::I32),
I32RemU => Validator::validate_binop(context, ValueType::I32),
I32And => Validator::validate_binop(context, ValueType::I32),
I32Or => Validator::validate_binop(context, ValueType::I32),
I32Xor => Validator::validate_binop(context, ValueType::I32),
I32Shl => Validator::validate_binop(context, ValueType::I32),
I32ShrS => Validator::validate_binop(context, ValueType::I32),
I32ShrU => Validator::validate_binop(context, ValueType::I32),
I32Rotl => Validator::validate_binop(context, ValueType::I32),
I32Rotr => Validator::validate_binop(context, ValueType::I32),
I64Clz => Validator::validate_unop(context, ValueType::I64),
I64Ctz => Validator::validate_unop(context, ValueType::I64),
I64Popcnt => Validator::validate_unop(context, ValueType::I64),
I64Add => Validator::validate_binop(context, ValueType::I64),
I64Sub => Validator::validate_binop(context, ValueType::I64),
I64Mul => Validator::validate_binop(context, ValueType::I64),
I64DivS => Validator::validate_binop(context, ValueType::I64),
I64DivU => Validator::validate_binop(context, ValueType::I64),
I64RemS => Validator::validate_binop(context, ValueType::I64),
I64RemU => Validator::validate_binop(context, ValueType::I64),
I64And => Validator::validate_binop(context, ValueType::I64),
I64Or => Validator::validate_binop(context, ValueType::I64),
I64Xor => Validator::validate_binop(context, ValueType::I64),
I64Shl => Validator::validate_binop(context, ValueType::I64),
I64ShrS => Validator::validate_binop(context, ValueType::I64),
I64ShrU => Validator::validate_binop(context, ValueType::I64),
I64Rotl => Validator::validate_binop(context, ValueType::I64),
I64Rotr => Validator::validate_binop(context, ValueType::I64),
F32Abs => Validator::validate_unop(context, ValueType::F32),
F32Neg => Validator::validate_unop(context, ValueType::F32),
F32Ceil => Validator::validate_unop(context, ValueType::F32),
F32Floor => Validator::validate_unop(context, ValueType::F32),
F32Trunc => Validator::validate_unop(context, ValueType::F32),
F32Nearest => Validator::validate_unop(context, ValueType::F32),
F32Sqrt => Validator::validate_unop(context, ValueType::F32),
F32Add => Validator::validate_binop(context, ValueType::F32),
F32Sub => Validator::validate_binop(context, ValueType::F32),
F32Mul => Validator::validate_binop(context, ValueType::F32),
F32Div => Validator::validate_binop(context, ValueType::F32),
F32Min => Validator::validate_binop(context, ValueType::F32),
F32Max => Validator::validate_binop(context, ValueType::F32),
F32Copysign => Validator::validate_binop(context, ValueType::F32),
F64Abs => Validator::validate_unop(context, ValueType::F64),
F64Neg => Validator::validate_unop(context, ValueType::F64),
F64Ceil => Validator::validate_unop(context, ValueType::F64),
F64Floor => Validator::validate_unop(context, ValueType::F64),
F64Trunc => Validator::validate_unop(context, ValueType::F64),
F64Nearest => Validator::validate_unop(context, ValueType::F64),
F64Sqrt => Validator::validate_unop(context, ValueType::F64),
F64Add => Validator::validate_binop(context, ValueType::F64),
F64Sub => Validator::validate_binop(context, ValueType::F64),
F64Mul => Validator::validate_binop(context, ValueType::F64),
F64Div => Validator::validate_binop(context, ValueType::F64),
F64Min => Validator::validate_binop(context, ValueType::F64),
F64Max => Validator::validate_binop(context, ValueType::F64),
F64Copysign => Validator::validate_binop(context, ValueType::F64),
I32WrapI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::I32),
I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32),
I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32),
I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32),
I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32),
I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64),
I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64),
I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64),
I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64),
I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64),
I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64),
F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32),
F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32),
F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32),
F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32),
F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::F32),
F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64),
F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64),
F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64),
F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64),
F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::F64),
I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32),
I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64),
F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32),
F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64),
}
}
fn validate_const(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<InstructionOutcome, Error> {
context.push_value(value_type.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_unop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type.into())?;
context.push_value(value_type.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_binop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type.into())?;
context.pop_value(value_type.into())?;
context.push_value(value_type.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_testop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type.into())?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_relop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type.into())?;
context.pop_value(value_type.into())?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: ValueType, value_type2: ValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type1.into())?;
context.push_value(value_type2.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_drop(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.pop_any_value().map(|_| ())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_select(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
let select_type = context.pop_any_value()?;
context.pop_value(select_type)?;
context.push_value(select_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
context.push_value(local_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
let value_type = context.pop_any_value()?;
if local_type != value_type {
return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
let value_type = context.tee_any_value()?;
if local_type != value_type {
return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let global_type: StackValueType = {
let global = context.module.require_global(index, None)?;
global.content_type().into()
};
context.push_value(global_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let global_type: StackValueType = {
let global = context.module.require_global(index, Some(true))?;
global.content_type().into()
};
let value_type = context.pop_any_value()?;
if global_type != value_type {
return Err(Error(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type)));
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result<InstructionOutcome, Error> {
if align != NATURAL_ALIGNMENT {
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)));
}
}
context.pop_value(ValueType::I32.into())?;
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
context.push_value(value_type.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result<InstructionOutcome, Error> {
if align != NATURAL_ALIGNMENT {
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)));
}
}
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
context.pop_value(value_type.into())?;
context.pop_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_block(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.push_label(BlockFrameType::Block, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_loop(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.push_label(BlockFrameType::Loop, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
context.push_label(BlockFrameType::IfTrue, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_else(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
let block_type = {
let top_frame = context.top_label()?;
if top_frame.frame_type != BlockFrameType::IfTrue {
return Err(Error("Misplaced else instruction".into()));
}
top_frame.block_type
};
context.pop_label()?;
if let BlockType::Value(value_type) = block_type {
context.pop_value(value_type.into())?;
}
context.push_label(BlockFrameType::IfFalse, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_end(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
{
let top_frame = context.top_label()?;
if top_frame.frame_type == BlockFrameType::IfTrue {
if top_frame.block_type != BlockType::NoResult {
return Err(Error(format!("If block without else required to have NoResult block type. But it have {:?} type", top_frame.block_type)));
}
}
}
context.pop_label().map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
let (frame_type, frame_block_type) = {
let frame = context.require_label(idx)?;
(frame.frame_type, frame.block_type)
};
if frame_type != BlockFrameType::Loop {
if let BlockType::Value(value_type) = frame_block_type {
context.tee_value(value_type.into())?;
}
}
Ok(InstructionOutcome::Unreachable)
}
fn validate_br_if(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
let (frame_type, frame_block_type) = {
let frame = context.require_label(idx)?;
(frame.frame_type, frame.block_type)
};
if frame_type != BlockFrameType::Loop {
if let BlockType::Value(value_type) = frame_block_type {
context.tee_value(value_type.into())?;
}
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_br_table(context: &mut FunctionValidationContext, table: &[u32], default: u32) -> Result<InstructionOutcome, Error> {
let mut required_block_type = None;
{
let default_block = context.require_label(default)?;
if default_block.frame_type != BlockFrameType::Loop {
required_block_type = Some(default_block.block_type);
}
for label in table {
let label_block = context.require_label(*label)?;
if label_block.frame_type != BlockFrameType::Loop {
if let Some(required_block_type) = required_block_type {
if 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 = Some(label_block.block_type);
}
}
}
context.pop_value(ValueType::I32.into())?;
if let Some(required_block_type) = required_block_type {
if let BlockType::Value(value_type) = required_block_type {
context.tee_value(value_type.into())?;
}
}
Ok(InstructionOutcome::Unreachable)
}
fn validate_return(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
if let BlockType::Value(value_type) = context.return_type()? {
context.tee_value(value_type.into())?;
}
Ok(InstructionOutcome::Unreachable)
}
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
let (argument_types, return_type) = context.module.require_function(idx)?;
for argument_type in argument_types.iter().rev() {
context.pop_value((*argument_type).into())?;
}
if let BlockType::Value(value_type) = return_type {
context.push_value(value_type.into())?;
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
{
let table = context.module.require_table(DEFAULT_TABLE_INDEX)?;
if table.elem_type() != TableElementType::AnyFunc {
return Err(Error(format!(
"Table {} has element type {:?} while `anyfunc` expected",
idx,
table.elem_type()
)));
}
}
context.pop_value(ValueType::I32.into())?;
let (argument_types, return_type) = context.module.require_function_type(idx)?;
for argument_type in argument_types.iter().rev() {
context.pop_value((*argument_type).into())?;
}
if let BlockType::Value(value_type) = return_type {
context.push_value(value_type.into())?;
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
context.pop_value(ValueType::I32.into())?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
}
impl<'a> FunctionValidationContext<'a> {
fn new(
module: &'a ModuleContext,
locals: &'a [ValueType],
value_stack_limit: usize,
frame_stack_limit: usize,
return_type: BlockType,
) -> Self {
FunctionValidationContext {
module: module,
position: 0,
locals: locals,
value_stack: StackWithLimit::with_limit(value_stack_limit),
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
return_type: Some(return_type),
labels: HashMap::new(),
}
}
fn push_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
Ok(self.value_stack.push(value_type.into())?)
}
fn pop_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
self.check_stack_access()?;
match self.value_stack.pop()? {
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()),
StackValueType::Any => Ok(()),
StackValueType::AnyUnlimited => {
self.value_stack.push(StackValueType::AnyUnlimited)?;
Ok(())
},
stack_value_type @ _ => Err(Error(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))),
}
}
fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
self.check_stack_access()?;
match *self.value_stack.top()? {
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()),
StackValueType::Any | StackValueType::AnyUnlimited => Ok(()),
stack_value_type @ _ => Err(Error(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))),
}
}
fn pop_any_value(&mut self) -> Result<StackValueType, Error> {
self.check_stack_access()?;
match self.value_stack.pop()? {
StackValueType::Specific(stack_value_type) => Ok(StackValueType::Specific(stack_value_type)),
StackValueType::Any => Ok(StackValueType::Any),
StackValueType::AnyUnlimited => {
self.value_stack.push(StackValueType::AnyUnlimited)?;
Ok(StackValueType::Any)
},
}
}
fn tee_any_value(&mut self) -> Result<StackValueType, Error> {
self.check_stack_access()?;
Ok(self.value_stack.top().map(Clone::clone)?)
}
fn unreachable(&mut self) -> Result<(), Error> {
Ok(self.value_stack.push(StackValueType::AnyUnlimited)?)
}
fn top_label(&self) -> Result<&BlockFrame, Error> {
Ok(self.frame_stack.top()?)
}
fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> {
Ok(self.frame_stack.push(BlockFrame {
frame_type: frame_type,
block_type: block_type,
begin_position: self.position,
branch_position: self.position,
end_position: self.position,
value_stack_len: self.value_stack.len(),
})?)
}
fn pop_label(&mut self) -> Result<InstructionOutcome, Error> {
let frame = self.frame_stack.pop()?;
let actual_value_type = if self.value_stack.len() > frame.value_stack_len {
Some(self.value_stack.pop()?)
} else {
None
};
self.value_stack.resize(frame.value_stack_len, StackValueType::Any);
match frame.block_type {
BlockType::NoResult if actual_value_type.map(|vt| vt.is_any_unlimited()).unwrap_or(true) => (),
BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) => (),
_ => return Err(Error(format!("Expected block to return {:?} while it has returned {:?}", frame.block_type, actual_value_type))),
}
if !self.frame_stack.is_empty() {
self.labels.insert(frame.begin_position, self.position);
}
if let BlockType::Value(value_type) = frame.block_type {
self.push_value(value_type.into())?;
}
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> {
Ok(self.frame_stack.get(idx as usize)?)
}
fn return_type(&self) -> Result<BlockType, Error> {
self.return_type.ok_or(Error("Trying to return from expression".into()))
}
fn require_local(&self, idx: u32) -> Result<StackValueType, Error> {
self.locals.get(idx as usize)
.cloned()
.map(Into::into)
.ok_or(Error(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len())))
}
fn check_stack_access(&self) -> Result<(), Error> {
let value_stack_min = self.frame_stack.top().expect("at least 1 topmost block").value_stack_len;
if self.value_stack.len() > value_stack_min {
Ok(())
} else {
Err(Error("Trying to access parent frame stack values.".into()))
}
}
}
impl StackValueType {
fn is_any(&self) -> bool {
match self {
&StackValueType::Any => true,
_ => false,
}
}
fn is_any_unlimited(&self) -> bool {
match self {
&StackValueType::AnyUnlimited => true,
_ => false,
}
}
fn value_type(&self) -> ValueType {
match self {
&StackValueType::Any | &StackValueType::AnyUnlimited => unreachable!("must be checked by caller"),
&StackValueType::Specific(value_type) => value_type,
}
}
}
impl From<ValueType> for StackValueType {
fn from(value_type: ValueType) -> Self {
StackValueType::Specific(value_type)
}
}
impl PartialEq<StackValueType> for StackValueType {
fn eq(&self, other: &StackValueType) -> bool {
if self.is_any() || other.is_any() || self.is_any_unlimited() || other.is_any_unlimited() {
true
} else {
self.value_type() == other.value_type()
}
}
}
impl PartialEq<ValueType> for StackValueType {
fn eq(&self, other: &ValueType) -> bool {
if self.is_any() || self.is_any_unlimited() {
true
} else {
self.value_type() == *other
}
}
}
impl PartialEq<StackValueType> for ValueType {
fn eq(&self, other: &StackValueType) -> bool {
other == self
}
}

View File

@ -1,326 +0,0 @@
use std::error;
use std::fmt;
use elements::{
BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType,
Module, Opcode, ResizableLimits, TableType, ValueType, InitExpr
};
use common::stack;
use self::context::ModuleContext;
use self::func::Validator;
pub use self::module::ValidatedModule;
mod context;
mod module;
mod func;
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub struct Error(String);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl error::Error for Error {
fn description(&self) -> &str {
&self.0
}
}
impl From<stack::Error> for Error {
fn from(e: stack::Error) -> Error {
Error(format!("Stack: {}", e))
}
}
pub fn validate_module(module: &Module) -> Result<ValidatedModule, Error> {
let context = prepare_context(module)?;
let function_section_len = module
.function_section()
.map(|s| s.entries().len())
.unwrap_or(0);
let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0);
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
)));
}
// 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");
// 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)))?;
Validator::validate_function(&context, function, function_body).map_err(|e| {
let Error(ref msg) = e;
Error(format!("Function #{} validation error: {}", index, msg))
})?;
}
}
// validate start section
if let Some(start_function) = module.start_section() {
let (params, return_ty) = context.require_function(start_function)?;
if return_ty != BlockType::NoResult || params.len() != 0 {
return Err(Error(
"start function expected to have type [] -> []".into(),
));
}
}
// validate export section
if let Some(export_section) = module.export_section() {
for export in export_section.entries() {
match *export.internal() {
Internal::Function(function_index) => {
context.require_function(function_index)?;
}
Internal::Global(global_index) => {
context.require_global(global_index, Some(false))?;
}
Internal::Memory(memory_index) => {
context.require_memory(memory_index)?;
}
Internal::Table(table_index) => {
context.require_table(table_index)?;
}
}
}
}
// validate import section
if let Some(import_section) = module.import_section() {
for import in import_section.entries() {
match *import.external() {
External::Function(function_type_index) => {
context.require_function(function_type_index)?;
},
External::Global(ref global_type) => {
if global_type.is_mutable() {
return Err(Error(format!("trying to import mutable global {}", import.field())));
}
},
External::Memory(ref memory_type) => {
memory_type.validate()?;
},
External::Table(ref table_type) => {
table_type.validate()?;
},
}
}
}
// there must be no greater than 1 table in tables index space
if context.tables().len() > 1 {
return Err(Error(format!("too many tables in index space: {}", context.tables().len())));
}
// there must be no greater than 1 linear memory in memory index space
if context.memories().len() > 1 {
return Err(Error(format!("too many memory regions in index space: {}", context.memories().len())));
}
// use data section to initialize linear memory regions
if let Some(data_section) = module.data_section() {
for data_segment in data_section.entries() {
context.require_memory(data_segment.index())?;
let init_ty = data_segment.offset().expr_const_type(context.globals())?;
if init_ty != ValueType::I32 {
return Err(Error("segment offset should return I32".into()));
}
}
}
// use element section to fill tables
if let Some(element_section) = module.elements_section() {
for element_segment in element_section.entries() {
context.require_table(element_segment.index())?;
let init_ty = element_segment.offset().expr_const_type(context.globals())?;
if init_ty != ValueType::I32 {
return Err(Error("segment offset should return I32".into()));
}
for function_index in element_segment.members() {
context.require_function(*function_index)?;
}
}
}
let ModuleContext {
types,
tables,
memories,
globals,
func_type_indexes,
} = context;
Ok(ValidatedModule {
types,
tables,
memories,
globals,
func_type_indexes,
})
}
fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
// Copy types from module as is.
let types = module
.type_section()
.map(|ts| ts.types().into_iter().cloned().collect())
.unwrap_or_default();
// Fill elements with imported values.
let mut func_type_indexes = Vec::new();
let mut tables = Vec::new();
let mut memories = Vec::new();
let mut globals = Vec::new();
for import_entry in module
.import_section()
.map(|i| i.entries())
.unwrap_or_default()
{
match *import_entry.external() {
External::Function(idx) => func_type_indexes.push(idx),
External::Table(ref table) => tables.push(table.clone()),
External::Memory(ref memory) => memories.push(memory.clone()),
External::Global(ref global) => globals.push(global.clone()),
}
}
// Concatenate elements with defined in the module.
if let Some(function_section) = module.function_section() {
for func_entry in function_section.entries() {
func_type_indexes.push(func_entry.type_ref());
}
}
if let Some(table_section) = module.table_section() {
for table_entry in table_section.entries() {
table_entry.validate()?;
tables.push(table_entry.clone());
}
}
if let Some(mem_section) = module.memory_section() {
for mem_entry in mem_section.entries() {
mem_entry.validate()?;
memories.push(mem_entry.clone());
}
}
if let Some(global_section) = module.global_section() {
// Validation of globals is defined over modified context C', which
// contains only imported globals. So we do globals validation
// in two passes, in first we validate globals and after all globals are validated
// add them in globals list.
for global_entry in global_section.entries() {
global_entry.validate(&globals)?;
}
for global_entry in global_section.entries() {
globals.push(global_entry.global_type().clone());
}
}
Ok(ModuleContext {
types,
tables,
memories,
globals,
func_type_indexes,
})
}
impl ResizableLimits {
fn validate(&self) -> Result<(), Error> {
if let Some(maximum) = self.maximum() {
if self.initial() > maximum {
return Err(Error(format!(
"maximum limit {} is lesser than minimum {}",
maximum,
self.initial()
)));
}
}
Ok(())
}
}
impl MemoryType {
fn validate(&self) -> Result<(), Error> {
self.limits().validate()
}
}
impl TableType {
fn validate(&self) -> Result<(), Error> {
self.limits().validate()
}
}
impl GlobalEntry {
fn validate(&self, globals: &[GlobalType]) -> Result<(), Error> {
let init = self.init_expr();
let init_expr_ty = init.expr_const_type(globals)?;
if init_expr_ty != self.global_type().content_type() {
return Err(Error(format!(
"Trying to initialize variable of type {:?} with value of type {:?}",
self.global_type().content_type(),
init_expr_ty
)));
}
Ok(())
}
}
impl InitExpr {
/// Returns type of this constant expression.
fn expr_const_type(&self, globals: &[GlobalType]) -> Result<ValueType, Error> {
let code = self.code();
if code.len() != 2 {
return Err(Error("Init expression should always be with length 2".into()));
}
let expr_ty: ValueType = match code[0] {
Opcode::I32Const(_) => ValueType::I32,
Opcode::I64Const(_) => ValueType::I64,
Opcode::F32Const(_) => ValueType::F32,
Opcode::F64Const(_) => ValueType::F64,
Opcode::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] != Opcode::End {
return Err(Error("Expression doesn't ends with `end` opcode".into()));
}
Ok(expr_ty)
}
}

View File

@ -1,10 +0,0 @@
use elements::{MemoryType, TableType, GlobalType, Type};
#[derive(Debug)]
pub struct ValidatedModule {
pub memories: Vec<MemoryType>,
pub tables: Vec<TableType>,
pub globals: Vec<GlobalType>,
pub types: Vec<Type>,
pub func_type_indexes: Vec<u32>,
}

View File

@ -1,301 +0,0 @@
use super::validate_module;
use builder::module;
use elements::{
External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType,
Opcode, Opcodes, TableType, ValueType, BlockType
};
#[test]
fn empty_is_valid() {
let module = module().build();
assert!(validate_module(&module).is_ok());
}
#[test]
fn limits() {
let test_cases = vec![
// min > max
(10, Some(9), false),
// min = max
(10, Some(10), true),
// table/memory is always valid without max
(10, None, true),
];
for (min, max, is_valid) in test_cases {
// defined table
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();
assert_eq!(validate_module(&m).is_ok(), is_valid);
// defined memory
let m = module()
.memory()
.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();
assert_eq!(validate_module(&m).is_ok(), is_valid);
}
}
#[test]
fn global_init_const() {
let m = module()
.with_global(
GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(
vec![Opcode::I32Const(42), Opcode::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![Opcode::I32Const(42), Opcode::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![Opcode::GetGlobal(0), Opcode::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![Opcode::GetGlobal(0), Opcode::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![Opcode::GetGlobal(0), Opcode::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![Opcode::I32Const(0), Opcode::End])
)
)
.with_global(
GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End])
)
)
.build();
assert!(validate_module(&m).is_err());
}
#[test]
fn global_init_misc() {
// without delimiting End opcode
let m = module()
.with_global(
GlobalEntry::new(
GlobalType::new(ValueType::I32, true),
InitExpr::new(vec![Opcode::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![Opcode::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![Opcode::Unreachable, Opcode::End])
)
)
.build();
assert!(validate_module(&m).is_err());
}
#[test]
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()
.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()
.build();
assert!(validate_module(&m).is_err());
}
#[test]
fn funcs() {
// recursive function calls is legal.
let m = module()
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::Call(1),
Opcode::End,
])).build()
.build()
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::Call(0),
Opcode::End,
])).build()
.build()
.build();
assert!(validate_module(&m).is_ok());
}
#[test]
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();
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();
assert!(validate_module(&m).is_err());
}
#[test]
fn if_else_with_return_type_validation() {
let m = module()
.function()
.signature().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::I32Const(1),
Opcode::If(BlockType::NoResult),
Opcode::I32Const(1),
Opcode::If(BlockType::Value(ValueType::I32)),
Opcode::I32Const(1),
Opcode::Else,
Opcode::I32Const(2),
Opcode::End,
Opcode::Drop,
Opcode::End,
Opcode::End,
])).build()
.build()
.build();
validate_module(&m).unwrap();
}