mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-21 18:51:52 +00:00
indirect function calls
This commit is contained in:
@ -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;
|
||||||
|
@ -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> {
|
||||||
|
@ -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]
|
||||||
|
@ -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
7
src/interpreter/utils.rs
Normal 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!()
|
||||||
|
}
|
@ -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 {
|
||||||
|
Reference in New Issue
Block a user