From 3be7051015efd76c93e17d7f225586d04813a31e Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 2 May 2017 08:37:48 +0300 Subject: [PATCH] callindirect_2 --- src/elements/types.rs | 2 + src/interpreter/module.rs | 106 +++++++++++++++++++--------------- src/interpreter/tests/wabt.rs | 24 ++++++-- 3 files changed, 81 insertions(+), 51 deletions(-) diff --git a/src/elements/types.rs b/src/elements/types.rs index e034f85..24248f1 100644 --- a/src/elements/types.rs +++ b/src/elements/types.rs @@ -5,6 +5,7 @@ use super::{ }; /// Type definition in types section. Currently can be only of the function type. +#[derive(Debug, PartialEq)] pub enum Type { /// Function type. Function(FunctionType), @@ -115,6 +116,7 @@ impl Serialize for BlockType { } /// Function signature type. +#[derive(Debug, PartialEq)] pub struct FunctionType { form: u8, params: Vec, diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index cdc154a..45ad264 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -172,59 +172,25 @@ impl ModuleInstance { pub fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result, Error> { 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()))))?; - - // 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; - let frame_stack_limit = outer.frame_stack_limit; - 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) - }, + ItemIndex::Internal(index) => self.call_internal_function(outer, index, None), 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())) - .and_then(|m| m.call_function(outer, ItemIndex::Internal(index))), + .and_then(|m| m.call_internal_function(outer, index, None)), } } /// Call function with given index in the given table. - pub fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, _type_index: u32, func_index: u32) -> Result, Error> { - // TODO: check signature + pub fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result, 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, + }; + match self.imports.parse_table_index(table_index) { ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), ItemIndex::Internal(table_index) => { @@ -233,7 +199,7 @@ impl ModuleInstance { 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)) + self.call_internal_function(outer, index, Some(function_type)) }, ItemIndex::External(table_index) => { let table = self.table(ItemIndex::External(table_index))?; @@ -246,10 +212,58 @@ impl ModuleInstance { .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)) + module.call_internal_function(outer, index, Some(function_type)) } } } + + /// Call function with internal index. + fn call_internal_function(&self, outer: CallerContext, index: u32, function_type: Option<&FunctionType>) -> Result, 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) + } } impl<'a> CallerContext<'a> { diff --git a/src/interpreter/tests/wabt.rs b/src/interpreter/tests/wabt.rs index ecd96d0..e7dcba2 100644 --- a/src/interpreter/tests/wabt.rs +++ b/src/interpreter/tests/wabt.rs @@ -721,6 +721,12 @@ fn callindirect_2() { ]); let body3 = Opcodes::new(vec![ + Opcode::GetLocal(0), + Opcode::I32Ctz, + Opcode::End, + ]); + + let body4 = Opcodes::new(vec![ Opcode::GetLocal(0), Opcode::GetLocal(1), Opcode::GetLocal(2), @@ -730,8 +736,8 @@ fn callindirect_2() { let module = module() .table() - .with_min(2) - .with_element(0, vec![0, 1]) + .with_min(3) + .with_element(0, vec![0, 1, 2]) .build() .function() .signature() @@ -747,6 +753,12 @@ fn callindirect_2() { .return_type().i32().build() .body().with_opcodes(body2).build() .build() + .function() + .signature() + .param().i32() + .return_type().i32().build() + .body().with_opcodes(body3).build() + .build() .function().main() .signature() .param().i32() @@ -754,7 +766,7 @@ fn callindirect_2() { .param().i32() .return_type().i32() .build() - .body().with_opcodes(body3).build() + .body().with_opcodes(body4).build() .build() .build(); @@ -763,5 +775,7 @@ fn callindirect_2() { assert_eq!(module.execute_main(vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(0)]).unwrap().unwrap(), RuntimeValue::I32(14)); assert_eq!(module.execute_main(vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(1)]).unwrap().unwrap(), RuntimeValue::I32(6)); assert_eq!(module.execute_main(vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(2)]).unwrap_err(), - Error::Table("trying to read table item with index 2 when there are only 2 items".into())); -} \ No newline at end of file + Error::Function("expected function with signature ([I32, I32]) -> Some(I32) when got with ([I32]) -> Some(I32)".into())); + assert_eq!(module.execute_main(vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(3)]).unwrap_err(), + Error::Table("trying to read table item with index 3 when there are only 3 items".into())); +}