266 lines
12 KiB
Rust
Raw Normal View History

2017-04-21 14:35:12 +03:00
use std::sync::{Arc, Weak};
2017-04-26 12:37:27 +03:00
use elements::{Module, InitExpr, Opcode, Type, FunctionType, FuncBody};
2017-04-21 14:35:12 +03:00
use interpreter::Error;
use interpreter::imports::ModuleImports;
use interpreter::memory::MemoryInstance;
use interpreter::program::ProgramInstanceEssence;
2017-04-26 12:37:27 +03:00
use interpreter::runner::{Interpreter, FunctionContext};
2017-04-21 14:35:12 +03:00
use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TryInto};
2017-04-26 15:41:22 +03:00
use interpreter::variable::{VariableInstance, VariableType};
2017-04-21 14:35:12 +03:00
/// Item index in items index space.
#[derive(Debug, Clone, Copy)]
pub enum ItemIndex {
/// Index in index space.
IndexSpace(u32),
/// Internal item index (i.e. index of item in items section).
Internal(u32),
/// External item index (i.e. index of item in export section).
External(u32),
}
/// Module instance.
pub struct ModuleInstance {
/// Module.
module: Module,
/// Module imports.
imports: ModuleImports,
/// Tables.
tables: Vec<Arc<TableInstance>>,
/// Linear memory regions.
memory: Vec<Arc<MemoryInstance>>,
/// Globals.
globals: Vec<Arc<VariableInstance>>,
}
impl ModuleInstance {
/// Instantiate given module within program context.
pub fn new(program: Weak<ProgramInstanceEssence>, module: Module) -> Result<Self, Error> {
// TODO: missing validation step
// load entries from import section
let imports = ModuleImports::new(program, module.import_section());
// instantiate linear memory regions, if any
let mut memory = match module.memory_section() {
Some(memory_section) => memory_section.entries()
.iter()
.map(MemoryInstance::new)
.collect::<Result<Vec<_>, _>>()?,
None => Vec::new(),
};
// instantiate tables, if any
let mut tables = match module.table_section() {
Some(table_section) => table_section.entries()
.iter()
2017-04-26 15:41:22 +03:00
.map(|tt| TableInstance::new(VariableType::AnyFunc, tt)) // TODO: actual table type
2017-04-21 14:35:12 +03:00
.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())
.map_err(|e| Error::Initialization(e.into()))
2017-04-26 12:37:27 +03:00
.and_then(|v| VariableInstance::new_global(g.global_type(), v).map(Arc::new))
2017-04-21 14:35:12 +03:00
})
.collect::<Result<Vec<_>, _>>()?,
None => Vec::new(),
};
// use data section to initialize linear memory regions
if let Some(data_section) = module.data_section() {
for (data_segement_index, data_segment) in data_section.entries().iter().enumerate() {
let offset: u32 = get_initializer(data_segment.offset())?.try_into()?;
memory
.get_mut(data_segment.index() as usize)
.ok_or(Error::Initialization(format!("DataSegment {} initializes non-existant MemoryInstance {}", data_segement_index, data_segment.index())))
.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) = module.elements_section() {
for (element_segment_index, element_segment) in element_section.entries().iter().enumerate() {
let offset: u32 = get_initializer(element_segment.offset())?.try_into()?;
tables
.get_mut(element_segment.index() as usize)
.ok_or(Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}", element_segment_index, element_segment.index())))
2017-04-26 15:41:22 +03:00
.and_then(|m| m.set_raw(offset, element_segment.members()).map_err(|e| Error::Initialization(e.into())))?;
2017-04-21 14:35:12 +03:00
}
}
Ok(ModuleInstance {
module: module,
imports: imports,
memory: memory,
tables: tables,
globals: globals,
})
}
/// Get module description reference.
pub fn module(&self) -> &Module {
&self.module
}
/// Get table reference.
pub 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(e)),
}
}
/// Get memory reference.
pub 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(e)),
}
}
/// Get global reference.
pub fn global(&self, index: ItemIndex) -> 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(e)),
}
}
/// Call function with given index in functions index space.
2017-04-26 15:41:22 +03:00
pub fn call_function(&self, outer: &mut FunctionContext, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> {
2017-04-21 14:35:12 +03:00
match self.imports.parse_function_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
ItemIndex::Internal(index) => {
// TODO: cache
// internal index = index of function in functions section && index of code in code section
// get function type index
let function_type_index = self.module
.functions_section()
.ok_or(Error::Function(format!("trying to call function with index {} in module without function section", index)))
.and_then(|s| s.entries()
.get(index as usize)
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions", index, s.entries().len()))))?
.type_ref();
// function type index = index of function type in types index
// get function type
let item_type = self.module
.type_section()
.ok_or(Error::Function(format!("trying to call function with index {} in module without types section", index)))
.and_then(|s| s.types()
.get(function_type_index as usize)
.ok_or(Error::Function(format!("trying to call function with type index {} in module with {} types", index, s.types().len()))))?;
let function_type = match item_type {
&Type::Function(ref function_type) => function_type,
};
// get function body
let function_body = self.module
.code_section()
.ok_or(Error::Function(format!("trying to call function with index {} in module without code section", index)))
.and_then(|s| s.bodies()
.get(index as usize)
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", index, s.bodies().len()))))?;
2017-04-26 12:37:27 +03:00
// TODO:
// each functions has its own value stack
// but there's global stack limit
// args, locals
let function_code = function_body.code().elements();
let value_stack_limit = outer.value_stack().limit() - outer.value_stack().len();
let frame_stack_limit = outer.frame_stack().limit() - outer.frame_stack().len();
let locals = prepare_function_locals(function_type, function_body, outer)?;
let mut innner = FunctionContext::new(self, value_stack_limit, frame_stack_limit, function_type, function_code, locals)?;
Interpreter::run_function(&mut innner, function_code)
2017-04-21 14:35:12 +03:00
},
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| self.imports.module(e.module()))
2017-04-26 12:37:27 +03:00
.and_then(|m| m.call_function(outer, ItemIndex::Internal(index))),
2017-04-21 14:35:12 +03:00
}
}
2017-04-26 15:41:22 +03:00
/// Call function with given index in the given table.
pub fn call_function_indirect(&self, outer: &mut FunctionContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result<Option<RuntimeValue>, Error> {
// TODO: check signature
match self.imports.parse_table_index(table_index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
ItemIndex::Internal(table_index) => {
let table = self.table(ItemIndex::Internal(table_index))?;
let index = match table.get(func_index)? {
RuntimeValue::AnyFunc(index) => index,
_ => return Err(Error::Function(format!("trying to indirect call function {} via non-anyfunc table {}", func_index, table_index))),
};
self.call_function(outer, ItemIndex::Internal(index))
},
ItemIndex::External(table_index) => {
let table = self.table(ItemIndex::External(table_index))?;
let index = match table.get(func_index)? {
RuntimeValue::AnyFunc(index) => index,
_ => return Err(Error::Function(format!("trying to indirect call function {} via non-anyfunc table {}", func_index, table_index))),
};
let module = self.module.import_section()
.ok_or(Error::Function(format!("trying to access external table with index {} in module without import section", table_index)))
.and_then(|s| s.entries().get(table_index as usize)
.ok_or(Error::Function(format!("trying to access external table with index {} in module with {}-entries import section", table_index, s.entries().len()))))
.and_then(|e| self.imports.module(e.module()))?;
module.call_function(outer, ItemIndex::Internal(index))
}
}
}
2017-04-21 14:35:12 +03:00
}
2017-04-26 12:37:27 +03:00
fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBody, outer: &mut FunctionContext) -> Result<Vec<VariableInstance>, Error> {
// locals = function arguments + defined locals
function_type.params().iter().rev()
.map(|param_type| {
let param_value = outer.value_stack_mut().pop()?;
let actual_type = param_value.variable_type();
let expected_type = (*param_type).into();
if actual_type != Some(expected_type) {
return Err(Error::Function(format!("invalid parameter type {:?} when expected {:?}", actual_type, expected_type)));
}
VariableInstance::new(true, expected_type, param_value)
})
.chain(function_body.locals().iter().map(|l| VariableInstance::new(true, l.value_type().into(), RuntimeValue::Null)))
.collect::<Result<Vec<_>, _>>()
}
2017-04-21 14:35:12 +03:00
fn get_initializer(expr: &InitExpr) -> Result<RuntimeValue, Error> {
let first_opcode = expr.code().get(0).ok_or(Error::Initialization(format!("empty instantiation-time initializer")))?;
match first_opcode {
// TODO: &Opcode::GetGlobal(index) => self.get_global(index),
&Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)),
&Opcode::I64Const(val) => Ok(RuntimeValue::I64(val)),
&Opcode::F32Const(val) => Ok(RuntimeValue::F32(val as f32)), // TODO
&Opcode::F64Const(val) => Ok(RuntimeValue::F64(val as f64)), // TODO
_ => Err(Error::Initialization(format!("not-supported {:?} instruction in instantiation-time initializer", first_opcode))),
}
}