mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-05 19:11:35 +00:00
Make it compile
This commit is contained in:
parent
339b909f86
commit
31abb05009
@ -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)))
|
||||
}
|
||||
}
|
@ -130,9 +130,7 @@ impl From<common::stack::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
mod validator;
|
||||
mod native;
|
||||
mod imports;
|
||||
mod memory;
|
||||
mod module;
|
||||
mod program;
|
||||
@ -147,10 +145,9 @@ mod store;
|
||||
mod tests;
|
||||
|
||||
pub use self::memory::MemoryInstance;
|
||||
pub use self::module::{ModuleInstance, ModuleInstanceInterface,
|
||||
ItemIndex, ExportEntryType, CallerContext, ExecutionParams, FunctionSignature};
|
||||
pub use self::module::{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};
|
||||
pub use self::native::{UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor};
|
||||
|
@ -5,12 +5,9 @@ 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::{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;
|
||||
@ -23,11 +20,9 @@ const DEFAULT_FRAME_STACK_LIMIT: usize = 1024;
|
||||
|
||||
/// Execution context.
|
||||
#[derive(Clone)]
|
||||
pub struct ExecutionParams<'a> {
|
||||
pub struct ExecutionParams {
|
||||
/// Arguments.
|
||||
pub args: Vec<RuntimeValue>,
|
||||
/// Execution-local external modules.
|
||||
pub externals: HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
|
||||
}
|
||||
|
||||
/// Export type.
|
||||
@ -50,34 +45,6 @@ pub enum FunctionSignature<'a> {
|
||||
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 {
|
||||
@ -89,26 +56,6 @@ pub enum ItemIndex {
|
||||
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
|
||||
@ -129,17 +76,7 @@ pub struct InternalFunction<'a> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecutionParams {
|
||||
/// Add argument.
|
||||
pub fn add_argument(mut self, arg: RuntimeValue) -> Self {
|
||||
self.args.push(arg);
|
||||
@ -147,493 +84,22 @@ impl<'a> ExecutionParams<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for ExecutionParams<'a> {
|
||||
impl Default for ExecutionParams {
|
||||
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> {
|
||||
impl<'a> From<Vec<RuntimeValue>> for ExecutionParams {
|
||||
fn from(args: Vec<RuntimeValue>) -> ExecutionParams {
|
||||
ExecutionParams {
|
||||
args: args,
|
||||
externals: HashMap::new(),
|
||||
args: args
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(|mt| MemoryInstance::new(mt).map(Arc::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).map(Arc::new))
|
||||
.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);
|
||||
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> {
|
||||
panic!("TODO")
|
||||
// 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> {
|
||||
// TODO:
|
||||
panic!()
|
||||
// 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>) -> Self {
|
||||
@ -664,34 +130,6 @@ pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
||||
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> {
|
||||
|
@ -5,7 +5,7 @@ use std::borrow::Cow;
|
||||
use parking_lot::RwLock;
|
||||
use elements::{Internal, ValueType};
|
||||
use interpreter::Error;
|
||||
use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex,
|
||||
use interpreter::module::{ExecutionParams, ItemIndex,
|
||||
CallerContext, ExportEntryType, InternalFunction, FunctionSignature};
|
||||
use interpreter::memory::MemoryInstance;
|
||||
use interpreter::table::TableInstance;
|
||||
@ -80,8 +80,6 @@ pub struct UserDefinedElements<E: UserFunctionExecutor> {
|
||||
|
||||
/// 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.
|
||||
@ -94,196 +92,6 @@ pub struct NativeModuleInstance<E: UserFunctionExecutor> {
|
||||
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, ExecutionParams, ExportEntryType,
|
||||
/// FunctionSignature, ItemIndex, ModuleInstance,
|
||||
/// ModuleInstanceInterface, ProgramInstance, RuntimeValue,
|
||||
/// UserDefinedElements, UserError, UserFunctionDescriptor,
|
||||
/// 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>()? as u32;
|
||||
/// let a = context.value_stack.pop_as::<u32>()? 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()
|
||||
|
@ -27,7 +27,7 @@ impl ProgramInstance {
|
||||
module: Module,
|
||||
start_exec_params: ExecutionParams,
|
||||
) -> Result<ModuleId, Error> {
|
||||
let extern_vals = Vec::new();
|
||||
let mut extern_vals = Vec::new();
|
||||
for import_entry in module.import_section().map(|s| s.entries()).unwrap_or(&[]) {
|
||||
let module = self.modules[import_entry.module()];
|
||||
let extern_val = module
|
||||
|
@ -493,15 +493,14 @@ impl<'store> Interpreter<'store> {
|
||||
) -> Result<InstructionOutcome, Error> {
|
||||
let global = context.module().resolve_global(&self.store, index);
|
||||
let val = self.store.read_global(global);
|
||||
context.value_stack_mut().push(val).map_err(Into::into)?;
|
||||
context.value_stack_mut().push(val)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_set_global<'a>(&mut self, context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
let val = context
|
||||
.value_stack_mut()
|
||||
.pop()
|
||||
.map_err(Into::into)?;
|
||||
.pop()?;
|
||||
|
||||
let global = context.module().resolve_global(&self.store, index);
|
||||
self.store.write_global(global, val)?;
|
||||
@ -516,7 +515,7 @@ impl<'store> Interpreter<'store> {
|
||||
.resolve(self.store);
|
||||
let b = m.get(address, mem::size_of::<T>())?;
|
||||
let n = T::from_little_endian(b)?;
|
||||
context.value_stack_mut().push(n.into()).map_err(Into::into)?;
|
||||
context.value_stack_mut().push(n.into())?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
@ -583,8 +582,7 @@ impl<'store> Interpreter<'store> {
|
||||
let s = m.size();
|
||||
context
|
||||
.value_stack_mut()
|
||||
.push(RuntimeValue::I32(s as i32))
|
||||
.map_err(Into::into)?;
|
||||
.push(RuntimeValue::I32(s as i32))?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
@ -596,8 +594,7 @@ impl<'store> Interpreter<'store> {
|
||||
let m = m.grow(pages)?;
|
||||
context
|
||||
.value_stack_mut()
|
||||
.push(RuntimeValue::I32(m as i32))
|
||||
.map_err(Into::into)?;
|
||||
.push(RuntimeValue::I32(m as i32))?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
@ -980,7 +977,7 @@ impl<'store> Interpreter<'store> {
|
||||
}
|
||||
|
||||
impl<'a> FunctionContext {
|
||||
pub fn new(store: &Store, function: FuncId, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionSignature, args: Vec<VariableInstance>) -> Self {
|
||||
pub fn new<'store>(store: &'store Store, function: FuncId, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionSignature, args: Vec<VariableInstance>) -> Self {
|
||||
let func_instance = function.resolve(store);
|
||||
let module = match *func_instance {
|
||||
FuncInstance::Defined { module, .. } => module,
|
||||
|
@ -263,7 +263,7 @@ impl Store {
|
||||
instance: &mut ModuleInstance,
|
||||
module_id: ModuleId,
|
||||
) -> Result<(), Error> {
|
||||
let aux_data = validate_module(module)?;
|
||||
let mut aux_data = validate_module(module)?;
|
||||
|
||||
for extern_val in extern_vals {
|
||||
match *extern_val {
|
||||
@ -440,14 +440,16 @@ impl Store {
|
||||
}
|
||||
|
||||
fn invoke(&mut self, func: FuncId, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
|
||||
let ExecutionParams { args, externals } = params;
|
||||
let ExecutionParams { args } = params;
|
||||
let mut args = StackWithLimit::with_data(args, DEFAULT_VALUE_STACK_LIMIT);
|
||||
let outer = CallerContext::topmost(&mut args);
|
||||
let func_type = func.resolve(self).func_type().resolve(self);
|
||||
let func_signature = FunctionSignature::Module(func_type);
|
||||
let args = prepare_function_args(&func_signature, outer.value_stack)?;
|
||||
let inner = FunctionContext::new(self, func, outer.value_stack_limit, outer.frame_stack_limit, &func_signature, args);
|
||||
let interpreter = Interpreter::new(self);
|
||||
let inner = {
|
||||
let func_type = func.resolve(self).func_type().resolve(self);
|
||||
let func_signature = FunctionSignature::Module(func_type);
|
||||
let args = prepare_function_args(&func_signature, outer.value_stack)?;
|
||||
FunctionContext::new(self, func, outer.value_stack_limit, outer.frame_stack_limit, &func_signature, args)
|
||||
};
|
||||
let mut interpreter = Interpreter::new(self);
|
||||
interpreter.run_function(inner)
|
||||
}
|
||||
|
||||
@ -462,8 +464,8 @@ impl Store {
|
||||
}
|
||||
|
||||
pub fn read_global(&self, global: GlobalId) -> RuntimeValue {
|
||||
let global_instance = self.globals.get_mut(global.0 as usize).expect("ID should be always valid");
|
||||
global_instance.val
|
||||
let global_instance = self.globals.get(global.0 as usize).expect("ID should be always valid");
|
||||
global_instance.val.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,808 +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::I32WarpI64 => 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: &Vec<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::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
|
||||
}
|
||||
}
|
@ -24,7 +24,5 @@ pub use elements::{
|
||||
|
||||
pub use interpreter::{
|
||||
ProgramInstance,
|
||||
ModuleInstance,
|
||||
ModuleInstanceInterface,
|
||||
RuntimeValue,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user