317 lines
14 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-28 13:34:58 +03:00
use interpreter::stack::StackWithLimit;
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>>,
}
2017-04-28 13:34:58 +03:00
/// Caller context.
pub struct CallerContext<'a> {
pub value_stack_limit: usize,
pub frame_stack_limit: usize,
pub value_stack: &'a mut StackWithLimit<RuntimeValue>,
}
2017-04-21 14:35:12 +03:00
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,
})
}
2017-04-27 14:44:03 +03:00
/// Execute start function of the module.
2017-04-28 13:34:58 +03:00
pub fn execute_main(&self, args: Vec<RuntimeValue>) -> Result<Option<RuntimeValue>, Error> {
let index = self.module.start_section().ok_or(Error::Program("module has no start section".into()))?;
let args_len = args.len();
let mut args = StackWithLimit::with_data(args, args_len);
let caller_context = CallerContext::topmost(&mut args);
self.call_function(caller_context, ItemIndex::IndexSpace(index))
2017-04-27 14:44:03 +03:00
}
2017-04-21 14:35:12 +03:00
/// 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-28 13:34:58 +03:00
pub fn call_function(&self, outer: CallerContext, 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"),
2017-05-02 08:37:48 +03:00
ItemIndex::Internal(index) => self.call_internal_function(outer, index, None),
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-05-02 08:37:48 +03:00
.and_then(|m| m.call_internal_function(outer, index, None)),
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.
2017-05-02 08:37:48 +03:00
pub fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result<Option<RuntimeValue>, Error> {
let function_type = match self.module.type_section()
.ok_or(Error::Function(format!("trying to indirect call function {} with non-existent function section", func_index)))
.and_then(|s| s.types().get(type_index as usize)
.ok_or(Error::Function(format!("trying to indirect call function {} with non-existent type index {}", func_index, type_index))))? {
&Type::Function(ref function_type) => function_type,
};
2017-04-26 15:41:22 +03:00
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))),
};
2017-05-02 08:37:48 +03:00
self.call_internal_function(outer, index, Some(function_type))
2017-04-26 15:41:22 +03:00
},
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()))?;
2017-05-02 08:37:48 +03:00
module.call_internal_function(outer, index, Some(function_type))
2017-04-26 15:41:22 +03:00
}
}
}
2017-05-02 08:37:48 +03:00
/// Call function with internal index.
fn call_internal_function(&self, outer: CallerContext, index: u32, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, Error> {
// 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 actual_function_type = match item_type {
&Type::Function(ref function_type) => function_type,
};
if let Some(ref function_type) = function_type {
if function_type != &actual_function_type {
return Err(Error::Function(format!("expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}",
function_type.params(), function_type.return_type(), actual_function_type.params(), actual_function_type.return_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()))))?;
// 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;
let frame_stack_limit = outer.frame_stack_limit;
let locals = prepare_function_locals(actual_function_type, function_body, outer)?;
let mut innner = FunctionContext::new(self, value_stack_limit, frame_stack_limit, actual_function_type, function_code, locals)?;
Interpreter::run_function(&mut innner, function_code)
}
2017-04-21 14:35:12 +03:00
}
2017-04-28 13:34:58 +03:00
impl<'a> CallerContext<'a> {
pub fn topmost(args: &'a mut StackWithLimit<RuntimeValue>) -> Self {
CallerContext {
value_stack_limit: 1024,
frame_stack_limit: 1024,
value_stack: args,
}
}
pub fn nested(outer: &'a mut FunctionContext) -> Self {
CallerContext {
value_stack_limit: outer.value_stack().limit() - outer.value_stack().len(),
frame_stack_limit: outer.frame_stack().limit() - outer.frame_stack().len(),
value_stack: outer.value_stack_mut(),
}
}
}
fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBody, outer: CallerContext) -> Result<Vec<VariableInstance>, Error> {
2017-04-26 12:37:27 +03:00
// locals = function arguments + defined locals
function_type.params().iter().rev()
.map(|param_type| {
2017-04-28 13:34:58 +03:00
let param_value = outer.value_stack.pop()?;
2017-04-26 12:37:27 +03:00
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)
})
2017-05-01 17:11:45 +03:00
.collect::<Vec<_>>().into_iter().rev()
// TODO: default values (zero), not null
2017-04-26 12:37:27 +03:00
.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))),
}
}