callindirect_2

This commit is contained in:
Svyatoslav Nikolsky 2017-05-02 08:37:48 +03:00
parent 9510789c91
commit 3be7051015
3 changed files with 81 additions and 51 deletions

View File

@ -5,6 +5,7 @@ use super::{
}; };
/// Type definition in types section. Currently can be only of the function type. /// Type definition in types section. Currently can be only of the function type.
#[derive(Debug, PartialEq)]
pub enum Type { pub enum Type {
/// Function type. /// Function type.
Function(FunctionType), Function(FunctionType),
@ -115,6 +116,7 @@ impl Serialize for BlockType {
} }
/// Function signature type. /// Function signature type.
#[derive(Debug, PartialEq)]
pub struct FunctionType { pub struct FunctionType {
form: u8, form: u8,
params: Vec<ValueType>, params: Vec<ValueType>,

View File

@ -172,7 +172,53 @@ impl ModuleInstance {
pub fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> { pub fn call_function(&self, outer: CallerContext, 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) => 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_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<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,
};
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_internal_function(outer, index, Some(function_type))
},
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_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<Option<RuntimeValue>, Error> {
// TODO: cache // TODO: cache
// internal index = index of function in functions section && index of code in code section // internal index = index of function in functions section && index of code in code section
// get function type index // get function type index
@ -191,9 +237,15 @@ impl ModuleInstance {
.and_then(|s| s.types() .and_then(|s| s.types()
.get(function_type_index as usize) .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()))))?; .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 { let actual_function_type = match item_type {
&Type::Function(ref function_type) => function_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 // get function body
let function_body = self.module let function_body = self.module
.code_section() .code_section()
@ -202,53 +254,15 @@ impl ModuleInstance {
.get(index as usize) .get(index as usize)
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", index, s.bodies().len()))))?; .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 // each functions has its own value stack
// but there's global stack limit // but there's global stack limit
// args, locals // args, locals
let function_code = function_body.code().elements(); let function_code = function_body.code().elements();
let value_stack_limit = outer.value_stack_limit; let value_stack_limit = outer.value_stack_limit;
let frame_stack_limit = outer.frame_stack_limit; let frame_stack_limit = outer.frame_stack_limit;
let locals = prepare_function_locals(function_type, function_body, outer)?; let locals = prepare_function_locals(actual_function_type, function_body, outer)?;
let mut innner = FunctionContext::new(self, value_stack_limit, frame_stack_limit, function_type, function_code, locals)?; 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) Interpreter::run_function(&mut innner, function_code)
},
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))),
}
}
/// 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<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))
}
}
} }
} }

View File

@ -721,6 +721,12 @@ fn callindirect_2() {
]); ]);
let body3 = Opcodes::new(vec![ let body3 = Opcodes::new(vec![
Opcode::GetLocal(0),
Opcode::I32Ctz,
Opcode::End,
]);
let body4 = Opcodes::new(vec![
Opcode::GetLocal(0), Opcode::GetLocal(0),
Opcode::GetLocal(1), Opcode::GetLocal(1),
Opcode::GetLocal(2), Opcode::GetLocal(2),
@ -730,8 +736,8 @@ fn callindirect_2() {
let module = module() let module = module()
.table() .table()
.with_min(2) .with_min(3)
.with_element(0, vec![0, 1]) .with_element(0, vec![0, 1, 2])
.build() .build()
.function() .function()
.signature() .signature()
@ -747,6 +753,12 @@ fn callindirect_2() {
.return_type().i32().build() .return_type().i32().build()
.body().with_opcodes(body2).build() .body().with_opcodes(body2).build()
.build() .build()
.function()
.signature()
.param().i32()
.return_type().i32().build()
.body().with_opcodes(body3).build()
.build()
.function().main() .function().main()
.signature() .signature()
.param().i32() .param().i32()
@ -754,7 +766,7 @@ fn callindirect_2() {
.param().i32() .param().i32()
.return_type().i32() .return_type().i32()
.build() .build()
.body().with_opcodes(body3).build() .body().with_opcodes(body4).build()
.build() .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(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(1)]).unwrap().unwrap(), RuntimeValue::I32(6));
assert_eq!(module.execute_main(vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(2)]).unwrap_err(), 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())); 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()));
} }