indirect function calls

This commit is contained in:
Svyatoslav Nikolsky
2017-04-26 15:41:22 +03:00
parent 0c794a4e90
commit 7beeb0587a
6 changed files with 107 additions and 23 deletions

View File

@ -13,7 +13,7 @@ pub enum Error {
Stack(String), Stack(String),
Value(String), Value(String),
Interpreter(String), Interpreter(String),
Trap, Trap(String),
NotImplemented, NotImplemented,
} }
@ -31,7 +31,7 @@ impl Into<String> for Error {
Error::Stack(s) => s, Error::Stack(s) => s,
Error::Interpreter(s) => s, Error::Interpreter(s) => s,
Error::Value(s) => s, Error::Value(s) => s,
Error::Trap => "trap".into(), Error::Trap(s) => format!("trap: {}", s),
Error::NotImplemented => "not implemented".into(), Error::NotImplemented => "not implemented".into(),
} }
} }
@ -44,5 +44,6 @@ mod program;
mod runner; mod runner;
mod stack; mod stack;
mod table; mod table;
mod utils;
mod value; mod value;
mod variable; mod variable;

View File

@ -7,7 +7,7 @@ use interpreter::program::ProgramInstanceEssence;
use interpreter::runner::{Interpreter, FunctionContext}; use interpreter::runner::{Interpreter, FunctionContext};
use interpreter::table::TableInstance; use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TryInto}; use interpreter::value::{RuntimeValue, TryInto};
use interpreter::variable::VariableInstance; use interpreter::variable::{VariableInstance, VariableType};
/// Item index in items index space. /// Item index in items index space.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -55,7 +55,7 @@ impl ModuleInstance {
let mut tables = match module.table_section() { let mut tables = match module.table_section() {
Some(table_section) => table_section.entries() Some(table_section) => table_section.entries()
.iter() .iter()
.map(TableInstance::new) .map(|tt| TableInstance::new(VariableType::AnyFunc, tt)) // TODO: actual table type
.collect::<Result<Vec<_>, _>>()?, .collect::<Result<Vec<_>, _>>()?,
None => Vec::new(), None => Vec::new(),
}; };
@ -91,7 +91,7 @@ impl ModuleInstance {
tables tables
.get_mut(element_segment.index() as usize) .get_mut(element_segment.index() as usize)
.ok_or(Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}", element_segment_index, element_segment.index()))) .ok_or(Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}", element_segment_index, element_segment.index())))
.and_then(|m| m.set(offset, element_segment.members()).map_err(|e| Error::Initialization(e.into())))?; .and_then(|m| m.set_raw(offset, element_segment.members()).map_err(|e| Error::Initialization(e.into())))?;
} }
} }
@ -152,7 +152,7 @@ impl ModuleInstance {
} }
/// Call function with given index in functions index space. /// Call function with given index in functions index space.
pub fn call_function<'a>(&self, outer: &mut FunctionContext<'a>, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> { pub fn call_function(&self, outer: &mut FunctionContext, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> {
match self.imports.parse_function_index(index) { match self.imports.parse_function_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
ItemIndex::Internal(index) => { ItemIndex::Internal(index) => {
@ -204,6 +204,35 @@ impl ModuleInstance {
.and_then(|m| m.call_function(outer, ItemIndex::Internal(index))), .and_then(|m| m.call_function(outer, ItemIndex::Internal(index))),
} }
} }
/// 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))
}
}
}
} }
fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBody, outer: &mut FunctionContext) -> Result<Vec<VariableInstance>, Error> { fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBody, outer: &mut FunctionContext) -> Result<Vec<VariableInstance>, Error> {

View File

@ -5,11 +5,13 @@ use elements::{Opcode, BlockType, FunctionType};
use interpreter::Error; use interpreter::Error;
use interpreter::module::{ModuleInstance, ItemIndex}; use interpreter::module::{ModuleInstance, ItemIndex};
use interpreter::stack::StackWithLimit; use interpreter::stack::StackWithLimit;
use interpreter::utils::{to_little_endian_bytes, from_little_endian_bytes};
use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto, use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto,
ArithmeticOps, Integer, Float}; ArithmeticOps, Integer, Float};
use interpreter::variable::VariableInstance; use interpreter::variable::VariableInstance;
const DEFAULT_MEMORY_INDEX: u32 = 0; const DEFAULT_MEMORY_INDEX: u32 = 0;
const DEFAULT_TABLE_INDEX: u32 = 0;
pub struct Interpreter; pub struct Interpreter;
@ -255,7 +257,7 @@ impl Interpreter {
} }
fn run_unreachable(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> { fn run_unreachable(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
Err(Error::Trap) Err(Error::Trap("programmatic".into()))
} }
fn run_nop(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> { fn run_nop(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
@ -322,11 +324,16 @@ impl Interpreter {
} }
fn run_call(context: &mut FunctionContext, func_idx: u32) -> Result<InstructionOutcome, Error> { fn run_call(context: &mut FunctionContext, func_idx: u32) -> Result<InstructionOutcome, Error> {
Err(Error::NotImplemented) context.call_function(func_idx)
.and_then(|r| r.map(|r| context.value_stack_mut().push(r)).unwrap_or(Ok(())))
.map(|_| InstructionOutcome::RunNextInstruction)
} }
fn run_call_indirect(context: &mut FunctionContext, type_idx: u32) -> Result<InstructionOutcome, Error> { fn run_call_indirect(context: &mut FunctionContext, type_idx: u32) -> Result<InstructionOutcome, Error> {
Err(Error::NotImplemented) let table_func_idx: u32 = context.value_stack_mut().pop_as()?;
context.call_function_indirect(DEFAULT_TABLE_INDEX, type_idx, table_func_idx)
.and_then(|r| r.map(|r| context.value_stack_mut().push(r)).unwrap_or(Ok(())))
.map(|_| InstructionOutcome::RunNextInstruction)
} }
fn run_drop(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> { fn run_drop(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
@ -863,6 +870,14 @@ impl<'a> FunctionContext<'a> {
self.module self.module
} }
pub fn call_function(&mut self, index: u32) -> Result<Option<RuntimeValue>, Error> {
self.module.call_function(self, ItemIndex::IndexSpace(index))
}
pub fn call_function_indirect(&mut self, table_index: u32, type_index: u32, func_index: u32) -> Result<Option<RuntimeValue>, Error> {
self.module.call_function_indirect(self, ItemIndex::IndexSpace(table_index), type_index, func_index)
}
pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result<InstructionOutcome, Error> { pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result<InstructionOutcome, Error> {
self.locals.get_mut(index) self.locals.get_mut(index)
.ok_or(Error::Local(format!("expected to have local with index {}", index))) .ok_or(Error::Local(format!("expected to have local with index {}", index)))
@ -936,14 +951,6 @@ fn effective_address(offset: u32, align: u32) -> Result<u32, Error> {
} }
} }
fn to_little_endian_bytes<T>(number: T) -> Vec<u8> {
unimplemented!()
}
fn from_little_endian_bytes<T>(buffer: &[u8]) -> T {
unimplemented!()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::Weak; use std::sync::Weak;
@ -970,7 +977,7 @@ mod tests {
Opcode::Unreachable, // trap Opcode::Unreachable, // trap
Opcode::End]); Opcode::End]);
assert_eq!(run_function_i32(&body, 0).unwrap_err(), Error::Trap); assert_eq!(run_function_i32(&body, 0).unwrap_err(), Error::Trap("programmatic".into()));
} }
#[test] #[test]

View File

@ -1,26 +1,56 @@
use std::u32; use std::u32;
use std::sync::Arc; use std::sync::Arc;
use parking_lot::RwLock;
use elements::TableType; use elements::TableType;
use interpreter::Error; use interpreter::Error;
use interpreter::variable::{VariableInstance, VariableType};
use interpreter::value::RuntimeValue; use interpreter::value::RuntimeValue;
/// Table instance. /// Table instance.
pub struct TableInstance { pub struct TableInstance {
/// Table variables type.
variable_type: VariableType,
/// Table memory buffer. /// Table memory buffer.
buffer: Vec<RuntimeValue>, buffer: RwLock<Vec<VariableInstance>>,
/// Maximum buffer size. /// Maximum buffer size.
maximum_size: u32, maximum_size: u32,
} }
impl TableInstance { impl TableInstance {
pub fn new(table_type: &TableType) -> Result<Arc<Self>, Error> { pub fn new(variable_type: VariableType, table_type: &TableType) -> Result<Arc<Self>, Error> {
Ok(Arc::new(TableInstance { Ok(Arc::new(TableInstance {
buffer: vec![RuntimeValue::Null; table_type.limits().initial() as usize], variable_type: variable_type,
buffer: RwLock::new(
vec![VariableInstance::new(true, variable_type, RuntimeValue::Null)?; table_type.limits().initial() as usize]
),
maximum_size: table_type.limits().maximum().unwrap_or(u32::MAX), maximum_size: table_type.limits().maximum().unwrap_or(u32::MAX),
})) }))
} }
pub fn set(&self, offset: u32, value: &[u32]) -> Result<Self, Error> { pub fn get(&self, offset: u32) -> Result<RuntimeValue, Error> {
unimplemented!() let buffer = self.buffer.read();
let buffer_len = buffer.len();
buffer.get(offset as usize)
.map(|v| v.get())
.ok_or(Error::Table(format!("trying to read table item with index {} when there are only {} items", offset, buffer_len)))
}
pub fn set_raw(&self, mut offset: u32, value: &[u32]) -> Result<(), Error> {
for val in value {
match self.variable_type {
VariableType::AnyFunc => self.set(offset, RuntimeValue::AnyFunc(*val))?,
_ => return Err(Error::NotImplemented),
}
offset += 1;
}
Ok(())
}
pub fn set(&self, offset: u32, value: RuntimeValue) -> Result<(), Error> {
let mut buffer = self.buffer.write();
let buffer_len = buffer.len();
buffer.get_mut(offset as usize)
.ok_or(Error::Table(format!("trying to update table item with index {} when there are only {} items", offset, buffer_len)))
.and_then(|v| v.set(value))
} }
} }

7
src/interpreter/utils.rs Normal file
View File

@ -0,0 +1,7 @@
pub fn to_little_endian_bytes<T>(number: T) -> Vec<u8> {
unimplemented!()
}
pub fn from_little_endian_bytes<T>(buffer: &[u8]) -> T {
unimplemented!()
}

View File

@ -63,6 +63,16 @@ impl VariableInstance {
} }
} }
impl Clone for VariableInstance {
fn clone(&self) -> Self {
VariableInstance {
is_mutable: self.is_mutable,
variable_type: self.variable_type,
value: RwLock::new(self.value.read().clone()),
}
}
}
impl From<ValueType> for VariableType { impl From<ValueType> for VariableType {
fn from(vt: ValueType) -> VariableType { fn from(vt: ValueType) -> VariableType {
match vt { match vt {