diff --git a/src/elements/export_entry.rs b/src/elements/export_entry.rs index 4f09c67..682d4e8 100644 --- a/src/elements/export_entry.rs +++ b/src/elements/export_entry.rs @@ -2,7 +2,7 @@ use std::io; use super::{Deserialize, Serialize, Error, VarUint7, VarUint32}; /// Internal reference of the exported entry. -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub enum Internal { /// Function reference. Function(u32), @@ -48,6 +48,7 @@ impl Serialize for Internal { } /// Export entry. +#[derive(Debug)] pub struct ExportEntry { field_str: String, internal: Internal, diff --git a/src/elements/import_entry.rs b/src/elements/import_entry.rs index 83328d3..a128d24 100644 --- a/src/elements/import_entry.rs +++ b/src/elements/import_entry.rs @@ -5,6 +5,7 @@ use super::{ }; /// Global definition struct +#[derive(Debug)] pub struct GlobalType { content_type: ValueType, is_mutable: bool, @@ -50,6 +51,7 @@ impl Serialize for GlobalType { } /// Table entry +#[derive(Debug)] pub struct TableType { elem_type: i8, limits: ResizableLimits, @@ -144,6 +146,7 @@ impl Serialize for ResizableLimits { } /// Memory entry. +#[derive(Debug)] pub struct MemoryType(ResizableLimits); impl MemoryType { @@ -174,6 +177,7 @@ impl Serialize for MemoryType { } /// External to local binding. +#[derive(Debug)] pub enum External { /// Binds to function with index. Function(u32), @@ -230,6 +234,7 @@ impl Serialize for External { } /// Import entry. +#[derive(Debug)] pub struct ImportEntry { module_str: String, field_str: String, diff --git a/src/elements/section.rs b/src/elements/section.rs index 4761d0c..700c580 100644 --- a/src/elements/section.rs +++ b/src/elements/section.rs @@ -220,7 +220,7 @@ impl Serialize for TypeSection { } /// Section of the imports definition. -#[derive(Default)] +#[derive(Debug, Default)] pub struct ImportSection(Vec); impl ImportSection { @@ -444,7 +444,7 @@ impl Serialize for GlobalSection { } /// List of exports definition. -#[derive(Default)] +#[derive(Debug, Default)] pub struct ExportSection(Vec); impl ExportSection { diff --git a/src/elements/segment.rs b/src/elements/segment.rs index fd399b1..a56cb5c 100644 --- a/src/elements/segment.rs +++ b/src/elements/segment.rs @@ -2,6 +2,7 @@ use std::io; use super::{Deserialize, Serialize, Error, VarUint32, CountedList, InitExpr, CountedListWriter}; /// Entry in the element section. +#[derive(Debug)] pub struct ElementSegment { index: u32, offset: InitExpr, diff --git a/src/interpreter/env.rs b/src/interpreter/env.rs index 2991973..77be563 100644 --- a/src/interpreter/env.rs +++ b/src/interpreter/env.rs @@ -1,18 +1,33 @@ use std::sync::Arc; use builder::module; -use elements::{Module, FunctionType, ExportEntry, Internal, MemoryType}; +use elements::{Module, FunctionType, ExportEntry, Internal, MemoryType, GlobalEntry, GlobalType, + ValueType, InitExpr, TableType, Opcode}; use interpreter::Error; use interpreter::module::{ModuleInstanceInterface, ItemIndex, CallerContext}; use interpreter::memory::MemoryInstance; use interpreter::table::TableInstance; use interpreter::value::RuntimeValue; -use interpreter::variable::VariableInstance; +use interpreter::variable::{VariableType, VariableInstance}; +const MEMORY_INDEX: u32 = 0; const MEMORY_LIMIT_MIN: u32 = 1; +const STACKTOP_INDEX: u32 = 0; +const STACKTOP_DEFAULT: i32 = 0; +const TABLE_BASE_INDEX: u32 = 0; +const TABLE_BASE_DEFAULT: i32 = 0; + +const INVOKE_VI_INDEX: u32 = 0; // (i32, i32) -> () +const INVOKE_INDEX: u32 = 1; // (i32) -> () + +const TABLE_SIZE: u32 = 1024; +const TABLE_INDEX: u32 = 0; + pub struct EnvModuleInstance { module: Module, memory: Arc, + stacktop: Arc, + table: Arc, } impl EnvModuleInstance { @@ -20,6 +35,8 @@ impl EnvModuleInstance { Ok(EnvModuleInstance { module: module, memory: MemoryInstance::new(&MemoryType::new(MEMORY_LIMIT_MIN, None))?, + stacktop: Arc::new(VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(STACKTOP_DEFAULT))?), + table: TableInstance::new(VariableType::AnyFunc, &TableType::new(TABLE_SIZE, None))?, }) } } @@ -29,7 +46,11 @@ impl ModuleInstanceInterface for EnvModuleInstance { unimplemented!() } - fn execute(&self, _index: u32, _args: Vec) -> Result, Error> { + fn execute_index(&self, _index: u32, _args: Vec) -> Result, Error> { + unimplemented!() + } + + fn execute_export(&self, _name: &str, _args: Vec) -> Result, Error> { unimplemented!() } @@ -37,19 +58,25 @@ impl ModuleInstanceInterface for EnvModuleInstance { &self.module } - fn table(&self, _index: ItemIndex) -> Result, Error> { - unimplemented!() + fn table(&self, index: ItemIndex) -> Result, Error> { + match &index { + &ItemIndex::Internal(TABLE_INDEX) => Ok(self.table.clone()), + _ => Err(Error::Env(format!("trying to get table with index {:?} from env module", index))), + } } fn memory(&self, index: ItemIndex) -> Result, Error> { match &index { - &ItemIndex::Internal(0) => Ok(self.memory.clone()), - _ => Err(Error::Env(format!("trying to get memory with index {:?}", index))), + &ItemIndex::Internal(MEMORY_INDEX) => Ok(self.memory.clone()), + _ => Err(Error::Env(format!("trying to get memory with index {:?} from env module", index))), } } - fn global(&self, _index: ItemIndex) -> Result, Error> { - unimplemented!() + fn global(&self, index: ItemIndex) -> Result, Error> { + match &index { + &ItemIndex::Internal(STACKTOP_INDEX) => Ok(self.stacktop.clone()), + _ => Err(Error::Env(format!("trying to get global with index {:?} from env module", index))), + } } fn call_function(&self, _outer: CallerContext, _index: ItemIndex) -> Result, Error> { @@ -67,10 +94,39 @@ impl ModuleInstanceInterface for EnvModuleInstance { pub fn env_module() -> Result { let module = module() - .memory() - .with_min(MEMORY_LIMIT_MIN) - .build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) + // memory regions + .memory().with_min(MEMORY_LIMIT_MIN).build() + .with_export(ExportEntry::new("memory".into(), Internal::Memory(MEMORY_INDEX))) + // tables + .table().with_min(TABLE_SIZE).build() + .with_export(ExportEntry::new("table".into(), Internal::Table(TABLE_INDEX))) + // globals + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(STACKTOP_DEFAULT)]))) + .with_export(ExportEntry::new("STACKTOP".into(), Internal::Global(STACKTOP_INDEX))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(TABLE_BASE_DEFAULT)]))) + .with_export(ExportEntry::new("tableBase".into(), Internal::Global(TABLE_BASE_INDEX))) + // functions + .with_export(ExportEntry::new("invoke_vi".into(), Internal::Function(INVOKE_VI_INDEX))) + .with_export(ExportEntry::new("invoke".into(), Internal::Function(INVOKE_INDEX))) .build(); + EnvModuleInstance::new(module) } +/* + (import "env" "STACKTOP" (global (;0;) i32)) + (import "env" "invoke_vi" (func (;0;) (type 3))) + (import "env" "invoke_v" (func (;1;) (type 1))) + (import "env" "_storage_size" (func (;2;) (type 2))) + (import "env" "_storage_write" (func (;3;) (type 4))) + (import "env" "_abort" (func (;4;) (type 0))) + (import "env" "_emscripten_memcpy_big" (func (;5;) (type 4))) + (import "env" "___resumeException" (func (;6;) (type 1))) + (import "env" "___cxa_find_matching_catch_2" (func (;7;) (type 2))) + (import "env" "___gxx_personality_v0" (func (;8;) (type 6))) + (import "env" "memory" (memory (;0;) 256 256)) + (import "env" "table" (table (;0;) 6 6 anyfunc)) + (import "env" "tableBase" (global (;1;) i32)) + (import "env" "gas" (func (;9;) (type 10))) + (import "env" "_free" (func (;10;) (type 1))) + (import "env" "_malloc" (func (;11;) (type 7))) +*/ \ No newline at end of file diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs index 86eb216..7b264b3 100644 --- a/src/interpreter/imports.rs +++ b/src/interpreter/imports.rs @@ -54,7 +54,7 @@ impl ModuleImports { match index { ItemIndex::IndexSpace(index) => match index.checked_sub(self.functions.len() as u32) { Some(index) => ItemIndex::Internal(index), - None => ItemIndex::External(index), + None => ItemIndex::External(self.functions[index as usize] as u32), }, index @ _ => index, } @@ -65,7 +65,7 @@ impl ModuleImports { match index { ItemIndex::IndexSpace(index) => match index.checked_sub(self.tables.len() as u32) { Some(index) => ItemIndex::Internal(index), - None => ItemIndex::External(index), + None => ItemIndex::External(self.tables[index as usize] as u32), }, index @ _ => index, } @@ -76,7 +76,7 @@ impl ModuleImports { match index { ItemIndex::IndexSpace(index) => match index.checked_sub(self.memory.len() as u32) { Some(index) => ItemIndex::Internal(index), - None => ItemIndex::External(index), + None => ItemIndex::External(self.memory[index as usize] as u32), }, index @ _ => index, } @@ -87,7 +87,7 @@ impl ModuleImports { match index { ItemIndex::IndexSpace(index) => match index.checked_sub(self.globals.len() as u32) { Some(index) => ItemIndex::Internal(index), - None => ItemIndex::External(index), + None => ItemIndex::External(self.globals[index as usize] as u32), }, index @ _ => index, } diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index fb07277..0e475b0 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -1,5 +1,5 @@ use std::sync::{Arc, Weak}; -use elements::{Module, InitExpr, Opcode, Type, FunctionType, FuncBody}; +use elements::{Module, InitExpr, Opcode, Type, FunctionType, FuncBody, Internal}; use interpreter::Error; use interpreter::imports::ModuleImports; use interpreter::memory::MemoryInstance; @@ -15,7 +15,9 @@ pub trait ModuleInstanceInterface { /// Execute start function of the module. fn execute_main(&self, args: Vec) -> Result, Error>; /// Execute function with the given index. - fn execute(&self, index: u32, args: Vec) -> Result, Error>; + fn execute_index(&self, index: u32, args: Vec) -> Result, Error>; + /// Execute function with the given export name. + fn execute_export(&self, name: &str, args: Vec) -> Result, Error>; /// Get module description reference. fn module(&self) -> &Module; /// Get table reference. @@ -39,7 +41,7 @@ pub enum ItemIndex { IndexSpace(u32), /// Internal item index (i.e. index of item in items section). Internal(u32), - /// External item index (i.e. index of item in export section). + /// External item index (i.e. index of item in the import section). External(u32), } @@ -145,16 +147,33 @@ impl ModuleInstance { impl ModuleInstanceInterface for ModuleInstance { fn execute_main(&self, args: Vec) -> Result, Error> { let index = self.module.start_section().ok_or(Error::Program("module has no start section".into()))?; - self.execute(index, args) + self.execute_index(index, args) } - fn execute(&self, index: u32, args: Vec) -> Result, Error> { + fn execute_index(&self, index: u32, args: Vec) -> Result, Error> { 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)) } + fn execute_export(&self, name: &str, args: Vec) -> Result, Error> { + let index = self.module.export_section() + .ok_or(Error::Function("missing export section".into())) + .and_then(|s| s.entries().iter() + .find(|e| e.field() == name && match e.internal() { + &Internal::Function(_) => true, + _ => false, + }) + .ok_or(Error::Function(format!("missing export section exported function with name {}", name))) + .map(|e| match e.internal() { + &Internal::Function(index) => index, + _ => unreachable!(), // checked couple of lines above + }) + )?; + self.execute_index(index, args) + } + fn module(&self) -> &Module { &self.module } @@ -333,12 +352,18 @@ fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBod fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports) -> Result { let first_opcode = expr.code().get(0).ok_or(Error::Initialization(format!("empty instantiation-time initializer")))?; match first_opcode { - &Opcode::GetGlobal(index) => module.import_section() - .ok_or(Error::Global(format!("trying to initialize with external global with index {} in module without import section", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Global(format!("trying to initialize with external global with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| imports.global(e)) - .map(|g| g.get()), + &Opcode::GetGlobal(index) => { + let index = match imports.parse_global_index(ItemIndex::IndexSpace(index)) { + ItemIndex::External(index) => index, + _ => return Err(Error::Global(format!("trying to initialize with non-external global {}", index))), + }; + module.import_section() + .ok_or(Error::Global(format!("trying to initialize with external global with index {} in module without import section", index))) + .and_then(|s| s.entries().get(index as usize) + .ok_or(Error::Global(format!("trying to initialize with external global with index {} in module with {}-entries import section", index, s.entries().len())))) + .and_then(|e| imports.global(e)) + .map(|g| g.get()) + }, &Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)), &Opcode::I64Const(val) => Ok(RuntimeValue::I64(val)), &Opcode::F32Const(val) => Ok(RuntimeValue::F32(val.transmute_into())), diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index e1de6c2..131706f 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -280,13 +280,11 @@ macro_rules! impl_try_truncate_into { impl TryTruncateInto<$into, Error> for $from { fn try_truncate_into(self) -> Result<$into, Error> { if !self.is_normal() { -println!("=== !IS_NORMAL: {}", self); return Err(Error::Value("invalid float value for this operation".into())); } let truncated = self.trunc(); if truncated < $into::MIN as $from || truncated > $into::MAX as $from { -println!("=== {} < {} || {} > {}", truncated, $into::MIN, truncated, $into::MIN); return Err(Error::Value("invalid float value for this operation".into())); }