diff --git a/src/builder/code.rs b/src/builder/code.rs index 710a377..5c86ccb 100644 --- a/src/builder/code.rs +++ b/src/builder/code.rs @@ -224,6 +224,7 @@ impl FuncBodyBuilder where F: Invoke { } pub struct FunctionDefinition { + pub is_main: bool, pub signature: Signature, pub code: elements::FuncBody, } @@ -231,6 +232,7 @@ pub struct FunctionDefinition { impl Default for FunctionDefinition { fn default() -> Self { FunctionDefinition { + is_main: false, signature: Signature::TypeReference(0), code: elements::FuncBody::empty(), } @@ -256,6 +258,11 @@ impl FunctionBuilder where F: Invoke { } } + pub fn main(mut self) -> Self { + self.func.is_main = true; + self + } + pub fn signature(self) -> SignatureBuilder { SignatureBuilder::with_callback(self) } diff --git a/src/builder/memory.rs b/src/builder/memory.rs new file mode 100644 index 0000000..af1a16d --- /dev/null +++ b/src/builder/memory.rs @@ -0,0 +1,49 @@ +use super::invoke::{Invoke, Identity}; + +pub struct MemoryDefinition { + pub min: u32, + pub max: Option, +} + +pub struct MemoryBuilder { + callback: F, + memory: MemoryDefinition, +} + +impl MemoryBuilder { + pub fn new() -> Self { + MemoryBuilder::with_callback(Identity) + } +} + +impl MemoryBuilder where F: Invoke { + pub fn with_callback(callback: F) -> Self { + MemoryBuilder { + callback: callback, + memory: Default::default(), + } + } + + pub fn with_min(mut self, min: u32) -> Self { + self.memory.min = min; + self + } + + pub fn with_max(mut self, max: Option) -> Self { + self.memory.max = max; + self + } + + pub fn build(self) -> F::Result { + self.callback.invoke(self.memory) + } +} + +impl Default for MemoryDefinition { + fn default() -> Self { + MemoryDefinition { + min: 1, + max: None, + } + } +} diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 06766b8..48fb4d2 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -5,6 +5,7 @@ mod module; mod code; mod misc; mod import; +mod memory; pub use self::module::{module, from_module, ModuleBuilder}; pub use self::code::{signatures, signature, function}; diff --git a/src/builder/module.rs b/src/builder/module.rs index 9902d46..3d5db5c 100644 --- a/src/builder/module.rs +++ b/src/builder/module.rs @@ -1,5 +1,6 @@ use super::invoke::{Invoke, Identity}; use super::code::{self, SignaturesBuilder, FunctionBuilder}; +use super::memory::{self, MemoryBuilder}; use super::import; use elements; @@ -190,12 +191,23 @@ impl ModuleBuilder where F: Invoke { self.module.code.bodies_mut().push(body); let body_index = self.module.code.bodies_mut().len() as u32 - 1; + if func.is_main { + self.module.start = Some(body_index); + } + CodeLocation { signature: signature_index, body: body_index, } } + /// Push linear memory region + pub fn push_memory(&mut self, memory: memory::MemoryDefinition) -> u32 { + let entries = self.module.memory.entries_mut(); + entries.push(elements::MemoryType::new(memory.min, memory.max)); + (entries.len() - 1) as u32 + } + fn resolve_type_ref(&mut self, signature: code::Signature) -> u32 { match signature { code::Signature::Inline(func_type) => { @@ -237,6 +249,11 @@ impl ModuleBuilder where F: Invoke { FunctionBuilder::with_callback(self) } + /// Add new linear memory using dedicated builder + pub fn memory(self) -> MemoryBuilder { + MemoryBuilder::with_callback(self) + } + /// Define functions section pub fn functions(self) -> SignaturesBuilder { SignaturesBuilder::with_callback(self) @@ -292,6 +309,18 @@ impl Invoke for ModuleBuilder } } +impl Invoke for ModuleBuilder + where F: Invoke +{ + type Result = Self; + + fn invoke(self, def: memory::MemoryDefinition) -> Self { + let mut b = self; + b.push_memory(def); + b + } +} + impl Invoke for ModuleBuilder where F: Invoke { diff --git a/src/elements/module.rs b/src/elements/module.rs index da7782f..e83dc60 100644 --- a/src/elements/module.rs +++ b/src/elements/module.rs @@ -129,6 +129,14 @@ impl Module { } None } + + /// Start section, if any. + pub fn start_section(&self) -> Option { + for section in self.sections() { + if let &Section::Start(sect) = section { return Some(sect); } + } + None + } } impl Deserialize for Module { diff --git a/src/elements/section.rs b/src/elements/section.rs index 19162fb..dc77a64 100644 --- a/src/elements/section.rs +++ b/src/elements/section.rs @@ -361,6 +361,11 @@ impl MemorySection { pub fn entries(&self) -> &[MemoryType] { &self.0 } + + /// Mutable list of all memory entries in the section + pub fn entries_mut(&mut self) -> &mut Vec { + &mut self.0 + } } impl Deserialize for MemorySection { diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 52241f6..e756b92 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -5,6 +5,7 @@ use interpreter::imports::ModuleImports; use interpreter::memory::MemoryInstance; use interpreter::program::ProgramInstanceEssence; use interpreter::runner::{Interpreter, FunctionContext}; +use interpreter::stack::StackWithLimit; use interpreter::table::TableInstance; use interpreter::value::{RuntimeValue, TryInto}; use interpreter::variable::{VariableInstance, VariableType}; @@ -34,6 +35,13 @@ pub struct ModuleInstance { globals: Vec>, } +/// Caller context. +pub struct CallerContext<'a> { + pub value_stack_limit: usize, + pub frame_stack_limit: usize, + pub value_stack: &'a mut StackWithLimit, +} + impl ModuleInstance { /// Instantiate given module within program context. pub fn new(program: Weak, module: Module) -> Result { @@ -105,8 +113,12 @@ impl ModuleInstance { } /// Execute start function of the module. - pub fn execute_main(&self, _args: &[RuntimeValue]) -> Result, Error> { - unimplemented!() + pub fn execute_main(&self, args: Vec) -> Result, 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)) } /// Get module description reference. @@ -157,7 +169,7 @@ impl ModuleInstance { } /// Call function with given index in functions index space. - pub fn call_function(&self, outer: &mut FunctionContext, index: ItemIndex) -> Result, Error> { + 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) => { @@ -195,8 +207,8 @@ impl ModuleInstance { // but there's global stack limit // args, locals let function_code = function_body.code().elements(); - let value_stack_limit = outer.value_stack().limit() - outer.value_stack().len(); - let frame_stack_limit = outer.frame_stack().limit() - outer.frame_stack().len(); + 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) @@ -211,7 +223,7 @@ impl ModuleInstance { } /// 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, Error> { + pub fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, _type_index: u32, func_index: u32) -> Result, Error> { // TODO: check signature match self.imports.parse_table_index(table_index) { ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), @@ -240,11 +252,29 @@ impl ModuleInstance { } } -fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBody, outer: &mut FunctionContext) -> Result, Error> { +impl<'a> CallerContext<'a> { + pub fn topmost(args: &'a mut StackWithLimit) -> 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, Error> { // locals = function arguments + defined locals function_type.params().iter().rev() .map(|param_type| { - let param_value = outer.value_stack_mut().pop()?; + let param_value = outer.value_stack.pop()?; let actual_type = param_value.variable_type(); let expected_type = (*param_type).into(); if actual_type != Some(expected_type) { diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index 126284d..93394fd 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -4,7 +4,7 @@ use std::u32; use std::fmt::Display; use elements::{Opcode, BlockType, FunctionType}; use interpreter::Error; -use interpreter::module::{ModuleInstance, ItemIndex}; +use interpreter::module::{ModuleInstance, CallerContext, ItemIndex}; use interpreter::stack::StackWithLimit; use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto, ArithmeticOps, Integer, Float, LittleEndianConvert}; @@ -880,11 +880,11 @@ impl<'a> FunctionContext<'a> { } pub fn call_function(&mut self, index: u32) -> Result, Error> { - self.module.call_function(self, ItemIndex::IndexSpace(index)) + self.module.call_function(CallerContext::nested(self), ItemIndex::IndexSpace(index)) } pub fn call_function_indirect(&mut self, table_index: u32, type_index: u32, func_index: u32) -> Result, Error> { - self.module.call_function_indirect(self, ItemIndex::IndexSpace(table_index), type_index, func_index) + self.module.call_function_indirect(CallerContext::nested(self), ItemIndex::IndexSpace(table_index), type_index, func_index) } pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result { diff --git a/src/interpreter/stack.rs b/src/interpreter/stack.rs index a0272a9..9436cce 100644 --- a/src/interpreter/stack.rs +++ b/src/interpreter/stack.rs @@ -12,6 +12,13 @@ pub struct StackWithLimit where T: Clone { } impl StackWithLimit where T: Clone { + pub fn with_data(data: Vec, limit: usize) -> Self { + StackWithLimit { + values: data.into_iter().collect(), + limit: limit, + } + } + pub fn with_limit(limit: usize) -> Self { StackWithLimit { values: VecDeque::new(), diff --git a/src/interpreter/tests/wabt.rs b/src/interpreter/tests/wabt.rs index 3a57a18..3df3a52 100644 --- a/src/interpreter/tests/wabt.rs +++ b/src/interpreter/tests/wabt.rs @@ -450,6 +450,42 @@ fn return_test() { /// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return-void.txt #[test] fn return_void() { + use builder::module; + use interpreter::module::ItemIndex; + use interpreter::program::ProgramInstance; + + let body = Opcodes::new(vec![ + Opcode::GetLocal(0), + Opcode::I32Const(0), + Opcode::I32Eq, + Opcode::If(BlockType::NoResult, + Opcodes::new(vec![ + Opcode::Return, + Opcode::End, + ])), + Opcode::I32Const(0), + Opcode::I32Const(1), + Opcode::I32Store(2, 0), + Opcode::End, + ]); + + let module = module() + .memory().build() + .function().main() + .signature().param().i32().build() + .body().with_opcodes(body).build() + .build() + .build(); + let program = ProgramInstance::new(); + let module = program.add_module("main", module).unwrap(); + + module.execute_main(vec![RuntimeValue::I32(0)]).unwrap(); + let memory = module.memory(ItemIndex::IndexSpace(0)).unwrap(); + assert_eq!(memory.get(0, 4).unwrap(), vec![0, 0, 0, 0]); + + module.execute_main(vec![RuntimeValue::I32(1)]).unwrap(); + let memory = module.memory(ItemIndex::IndexSpace(0)).unwrap(); + assert_eq!(memory.get(0, 4).unwrap(), vec![0, 0, 0, 1]); // TODO: linear memory required }