This commit is contained in:
Svyatoslav Nikolsky
2017-04-28 13:34:58 +03:00
parent 51c42b653c
commit e14e3e055a
10 changed files with 183 additions and 11 deletions

View File

@ -224,6 +224,7 @@ impl<F> FuncBodyBuilder<F> where F: Invoke<elements::FuncBody> {
} }
pub struct FunctionDefinition { pub struct FunctionDefinition {
pub is_main: bool,
pub signature: Signature, pub signature: Signature,
pub code: elements::FuncBody, pub code: elements::FuncBody,
} }
@ -231,6 +232,7 @@ pub struct FunctionDefinition {
impl Default for FunctionDefinition { impl Default for FunctionDefinition {
fn default() -> Self { fn default() -> Self {
FunctionDefinition { FunctionDefinition {
is_main: false,
signature: Signature::TypeReference(0), signature: Signature::TypeReference(0),
code: elements::FuncBody::empty(), code: elements::FuncBody::empty(),
} }
@ -256,6 +258,11 @@ impl<F> FunctionBuilder<F> where F: Invoke<FunctionDefinition> {
} }
} }
pub fn main(mut self) -> Self {
self.func.is_main = true;
self
}
pub fn signature(self) -> SignatureBuilder<Self> { pub fn signature(self) -> SignatureBuilder<Self> {
SignatureBuilder::with_callback(self) SignatureBuilder::with_callback(self)
} }

49
src/builder/memory.rs Normal file
View File

@ -0,0 +1,49 @@
use super::invoke::{Invoke, Identity};
pub struct MemoryDefinition {
pub min: u32,
pub max: Option<u32>,
}
pub struct MemoryBuilder<F=Identity> {
callback: F,
memory: MemoryDefinition,
}
impl MemoryBuilder {
pub fn new() -> Self {
MemoryBuilder::with_callback(Identity)
}
}
impl<F> MemoryBuilder<F> where F: Invoke<MemoryDefinition> {
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<u32>) -> 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,
}
}
}

View File

@ -5,6 +5,7 @@ mod module;
mod code; mod code;
mod misc; mod misc;
mod import; mod import;
mod memory;
pub use self::module::{module, from_module, ModuleBuilder}; pub use self::module::{module, from_module, ModuleBuilder};
pub use self::code::{signatures, signature, function}; pub use self::code::{signatures, signature, function};

View File

@ -1,5 +1,6 @@
use super::invoke::{Invoke, Identity}; use super::invoke::{Invoke, Identity};
use super::code::{self, SignaturesBuilder, FunctionBuilder}; use super::code::{self, SignaturesBuilder, FunctionBuilder};
use super::memory::{self, MemoryBuilder};
use super::import; use super::import;
use elements; use elements;
@ -190,12 +191,23 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
self.module.code.bodies_mut().push(body); self.module.code.bodies_mut().push(body);
let body_index = self.module.code.bodies_mut().len() as u32 - 1; let body_index = self.module.code.bodies_mut().len() as u32 - 1;
if func.is_main {
self.module.start = Some(body_index);
}
CodeLocation { CodeLocation {
signature: signature_index, signature: signature_index,
body: body_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 { fn resolve_type_ref(&mut self, signature: code::Signature) -> u32 {
match signature { match signature {
code::Signature::Inline(func_type) => { code::Signature::Inline(func_type) => {
@ -237,6 +249,11 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
FunctionBuilder::with_callback(self) FunctionBuilder::with_callback(self)
} }
/// Add new linear memory using dedicated builder
pub fn memory(self) -> MemoryBuilder<Self> {
MemoryBuilder::with_callback(self)
}
/// Define functions section /// Define functions section
pub fn functions(self) -> SignaturesBuilder<Self> { pub fn functions(self) -> SignaturesBuilder<Self> {
SignaturesBuilder::with_callback(self) SignaturesBuilder::with_callback(self)
@ -292,6 +309,18 @@ impl<F> Invoke<code::FunctionDefinition> for ModuleBuilder<F>
} }
} }
impl<F> Invoke<memory::MemoryDefinition> for ModuleBuilder<F>
where F: Invoke<elements::Module>
{
type Result = Self;
fn invoke(self, def: memory::MemoryDefinition) -> Self {
let mut b = self;
b.push_memory(def);
b
}
}
impl<F> Invoke<elements::ImportEntry> for ModuleBuilder<F> impl<F> Invoke<elements::ImportEntry> for ModuleBuilder<F>
where F: Invoke<elements::Module> where F: Invoke<elements::Module>
{ {

View File

@ -129,6 +129,14 @@ impl Module {
} }
None None
} }
/// Start section, if any.
pub fn start_section(&self) -> Option<u32> {
for section in self.sections() {
if let &Section::Start(sect) = section { return Some(sect); }
}
None
}
} }
impl Deserialize for Module { impl Deserialize for Module {

View File

@ -361,6 +361,11 @@ impl MemorySection {
pub fn entries(&self) -> &[MemoryType] { pub fn entries(&self) -> &[MemoryType] {
&self.0 &self.0
} }
/// Mutable list of all memory entries in the section
pub fn entries_mut(&mut self) -> &mut Vec<MemoryType> {
&mut self.0
}
} }
impl Deserialize for MemorySection { impl Deserialize for MemorySection {

View File

@ -5,6 +5,7 @@ use interpreter::imports::ModuleImports;
use interpreter::memory::MemoryInstance; use interpreter::memory::MemoryInstance;
use interpreter::program::ProgramInstanceEssence; use interpreter::program::ProgramInstanceEssence;
use interpreter::runner::{Interpreter, FunctionContext}; use interpreter::runner::{Interpreter, FunctionContext};
use interpreter::stack::StackWithLimit;
use interpreter::table::TableInstance; use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TryInto}; use interpreter::value::{RuntimeValue, TryInto};
use interpreter::variable::{VariableInstance, VariableType}; use interpreter::variable::{VariableInstance, VariableType};
@ -34,6 +35,13 @@ pub struct ModuleInstance {
globals: Vec<Arc<VariableInstance>>, globals: Vec<Arc<VariableInstance>>,
} }
/// Caller context.
pub struct CallerContext<'a> {
pub value_stack_limit: usize,
pub frame_stack_limit: usize,
pub value_stack: &'a mut StackWithLimit<RuntimeValue>,
}
impl ModuleInstance { impl ModuleInstance {
/// Instantiate given module within program context. /// Instantiate given module within program context.
pub fn new(program: Weak<ProgramInstanceEssence>, module: Module) -> Result<Self, Error> { pub fn new(program: Weak<ProgramInstanceEssence>, module: Module) -> Result<Self, Error> {
@ -105,8 +113,12 @@ impl ModuleInstance {
} }
/// Execute start function of the module. /// Execute start function of the module.
pub fn execute_main(&self, _args: &[RuntimeValue]) -> Result<Option<RuntimeValue>, Error> { pub fn execute_main(&self, args: Vec<RuntimeValue>) -> Result<Option<RuntimeValue>, Error> {
unimplemented!() 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. /// Get module description reference.
@ -157,7 +169,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(&self, outer: &mut FunctionContext, 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) => {
@ -195,8 +207,8 @@ impl ModuleInstance {
// 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() - outer.value_stack().len(); let value_stack_limit = outer.value_stack_limit;
let frame_stack_limit = outer.frame_stack().limit() - outer.frame_stack().len(); let frame_stack_limit = outer.frame_stack_limit;
let locals = prepare_function_locals(function_type, function_body, outer)?; 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)?; let mut innner = FunctionContext::new(self, value_stack_limit, frame_stack_limit, function_type, function_code, locals)?;
Interpreter::run_function(&mut innner, function_code) Interpreter::run_function(&mut innner, function_code)
@ -211,7 +223,7 @@ impl ModuleInstance {
} }
/// Call function with given index in the given table. /// 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> { pub fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, _type_index: u32, func_index: u32) -> Result<Option<RuntimeValue>, Error> {
// TODO: check signature // TODO: check signature
match self.imports.parse_table_index(table_index) { match self.imports.parse_table_index(table_index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), 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<Vec<VariableInstance>, Error> { impl<'a> CallerContext<'a> {
pub fn topmost(args: &'a mut StackWithLimit<RuntimeValue>) -> 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<Vec<VariableInstance>, Error> {
// locals = function arguments + defined locals // locals = function arguments + defined locals
function_type.params().iter().rev() function_type.params().iter().rev()
.map(|param_type| { .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 actual_type = param_value.variable_type();
let expected_type = (*param_type).into(); let expected_type = (*param_type).into();
if actual_type != Some(expected_type) { if actual_type != Some(expected_type) {

View File

@ -4,7 +4,7 @@ use std::u32;
use std::fmt::Display; use std::fmt::Display;
use elements::{Opcode, BlockType, FunctionType}; use elements::{Opcode, BlockType, FunctionType};
use interpreter::Error; use interpreter::Error;
use interpreter::module::{ModuleInstance, ItemIndex}; use interpreter::module::{ModuleInstance, CallerContext, ItemIndex};
use interpreter::stack::StackWithLimit; use interpreter::stack::StackWithLimit;
use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto, use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto,
ArithmeticOps, Integer, Float, LittleEndianConvert}; ArithmeticOps, Integer, Float, LittleEndianConvert};
@ -880,11 +880,11 @@ impl<'a> FunctionContext<'a> {
} }
pub fn call_function(&mut self, index: u32) -> Result<Option<RuntimeValue>, Error> { pub fn call_function(&mut self, index: u32) -> Result<Option<RuntimeValue>, 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<Option<RuntimeValue>, Error> { 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) 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<InstructionOutcome, Error> { pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result<InstructionOutcome, Error> {

View File

@ -12,6 +12,13 @@ pub struct StackWithLimit<T> where T: Clone {
} }
impl<T> StackWithLimit<T> where T: Clone { impl<T> StackWithLimit<T> where T: Clone {
pub fn with_data(data: Vec<T>, limit: usize) -> Self {
StackWithLimit {
values: data.into_iter().collect(),
limit: limit,
}
}
pub fn with_limit(limit: usize) -> Self { pub fn with_limit(limit: usize) -> Self {
StackWithLimit { StackWithLimit {
values: VecDeque::new(), values: VecDeque::new(),

View File

@ -450,6 +450,42 @@ fn return_test() {
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return-void.txt /// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return-void.txt
#[test] #[test]
fn return_void() { 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 // TODO: linear memory required
} }