diff --git a/spec/src/run.rs b/spec/src/run.rs index 8800345..f212bdb 100644 --- a/spec/src/run.rs +++ b/spec/src/run.rs @@ -110,7 +110,7 @@ fn run_action(program: &ProgramInstance, action: &test::Action) elements::Internal::Global(global_index) => Ok(ItemIndex::IndexSpace(global_index)), _ => Err(InterpreterError::Global(format!("Expected to have exported global with name {}", field))), }) - .and_then(|g| module.global(g, None).map(|g| Some(g.get()))) + .and_then(|g| module.global(g, None, None).map(|g| Some(g.get()))) } } } diff --git a/src/interpreter/env.rs b/src/interpreter/env.rs index 436610c..6748f2b 100644 --- a/src/interpreter/env.rs +++ b/src/interpreter/env.rs @@ -116,8 +116,8 @@ impl ModuleInstanceInterface for EnvModuleInstance { self.instance.memory(index) } - fn global(&self, index: ItemIndex, variable_type: Option) -> Result, Error> { - self.instance.global(index, variable_type) + fn global<'a>(&self, index: ItemIndex, variable_type: Option, externals: Option<&'a HashMap>>) -> Result, Error> { + self.instance.global(index, variable_type, externals) } fn function_type(&self, function_index: ItemIndex) -> Result { @@ -143,19 +143,19 @@ impl ModuleInstanceInterface for EnvModuleInstance { fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result, Error> { // to make interpreter independent of *SCRIPTEN runtime, just make abort/assert = interpreter Error match index { - INDEX_FUNC_ABORT => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT), Some(VariableType::I32)) + INDEX_FUNC_ABORT => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT), Some(VariableType::I32), None) .and_then(|g| g.set(RuntimeValue::I32(1))) .and_then(|_| Err(Error::Trap("abort".into()))), INDEX_FUNC_ASSERT => outer.value_stack.pop_as::() .and_then(|condition| if condition == 0 { - self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT), Some(VariableType::I32)) + self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT), Some(VariableType::I32), None) .and_then(|g| g.set(RuntimeValue::I32(1))) .and_then(|_| Err(Error::Trap("assertion failed".into()))) } else { Ok(None) }), INDEX_FUNC_ENLARGE_MEMORY => Ok(Some(RuntimeValue::I32(0))), // TODO: support memory enlarge - INDEX_FUNC_GET_TOTAL_MEMORY => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_TOTAL_MEMORY), Some(VariableType::I32)) + INDEX_FUNC_GET_TOTAL_MEMORY => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_TOTAL_MEMORY), Some(VariableType::I32), None) .map(|g| g.get()) .map(Some), INDEX_FUNC_MIN_NONUSED ... INDEX_FUNC_MAX => Err(Error::Trap("unimplemented".into())), diff --git a/src/interpreter/env_native.rs b/src/interpreter/env_native.rs index 860fd2e..921067f 100644 --- a/src/interpreter/env_native.rs +++ b/src/interpreter/env_native.rs @@ -13,6 +13,8 @@ use interpreter::variable::{VariableInstance, VariableType}; /// Min index of native function. pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001; +/// Min index of native global. +pub const NATIVE_INDEX_GLOBAL_MIN: u32 = 20001; /// User functions executor. pub trait UserFunctionExecutor { @@ -65,12 +67,14 @@ impl UserFunctionDescriptor { } } -/// Set of user-defined functions -pub struct UserFunctions<'a> { - /// Functions list. +/// Set of user-defined module elements. +pub struct UserDefinedElements<'a> { + /// User globals list. + pub globals: HashMap>, + /// User functions list. pub functions: Cow<'static, [UserFunctionDescriptor]>, /// Functions executor. - pub executor: &'a mut UserFunctionExecutor, + pub executor: Option<&'a mut UserFunctionExecutor>, } /// Native module instance. @@ -78,21 +82,31 @@ pub struct NativeModuleInstance<'a> { /// Underllying module reference. env: Arc, /// User function executor. - executor: RwLock<&'a mut UserFunctionExecutor>, + executor: RwLock>, /// By-name functions index. - by_name: HashMap, + functions_by_name: HashMap, /// User functions list. functions: Cow<'static, [UserFunctionDescriptor]>, + /// By-name functions index. + globals_by_name: HashMap, + /// User globals list. + globals: Vec>, } impl<'a> NativeModuleInstance<'a> { /// Create new native module - pub fn new(env: Arc, functions: UserFunctions<'a>) -> Result { + pub fn new(env: Arc, elements: UserDefinedElements<'a>) -> Result { + if !elements.functions.is_empty() && elements.executor.is_none() { + return Err(Error::Function("trying to construct native env module with functions, but without executor".into())); + } + Ok(NativeModuleInstance { env: env, - executor: RwLock::new(functions.executor), - by_name: functions.functions.iter().enumerate().map(|(i, f)| (f.name().to_owned(), i as u32)).collect(), - functions: functions.functions, + executor: RwLock::new(elements.executor), + functions_by_name: elements.functions.iter().enumerate().map(|(i, f)| (f.name().to_owned(), i as u32)).collect(), + functions: elements.functions, + globals_by_name: elements.globals.iter().enumerate().map(|(i, (g_name, _))| (g_name.to_owned(), i as u32)).collect(), + globals: elements.globals.into_iter().map(|(_, g)| g).collect(), }) } } @@ -107,9 +121,10 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { } fn export_entry<'b>(&self, name: &str, required_type: &ExportEntryType) -> Result { - if let Some(index) = self.by_name.get(name) { + if let Some(index) = self.functions_by_name.get(name) { let composite_index = NATIVE_INDEX_FUNC_MIN + *index; match required_type { + &ExportEntryType::Any => return Ok(Internal::Function(composite_index)), &ExportEntryType::Function(ref required_type) if self.function_type(ItemIndex::Internal(composite_index)) .expect("by_name contains index; function_type succeeds for all functions from by_name; qed") == *required_type @@ -117,6 +132,17 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { _ => (), } } + if let Some(index) = self.globals_by_name.get(name) { + match required_type { + &ExportEntryType::Any => return Ok(Internal::Global(NATIVE_INDEX_GLOBAL_MIN + *index)), + &ExportEntryType::Global(ref required_type) + if self.globals.get(*index as usize) + .expect("globals_by_name maps to indexes of globals; index read from globals_by_name; qed") + .variable_type() == *required_type + => return Ok(Internal::Global(NATIVE_INDEX_GLOBAL_MIN + *index)), + _ => (), + } + } self.env.export_entry(name, required_type) } @@ -129,8 +155,20 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { self.env.memory(index) } - fn global(&self, index: ItemIndex, variable_type: Option) -> Result, Error> { - self.env.global(index, variable_type) + fn global<'b>(&self, global_index: ItemIndex, variable_type: Option, externals: Option<&'b HashMap>>) -> Result, Error> { + let index = match global_index { + ItemIndex::IndexSpace(index) | ItemIndex::Internal(index) => index, + ItemIndex::External(_) => unreachable!("trying to get global, exported by native env module"), + }; + + if index < NATIVE_INDEX_GLOBAL_MIN { + return self.env.global(global_index, variable_type, externals); + } + + self.globals + .get((index - NATIVE_INDEX_GLOBAL_MIN) as usize) + .cloned() + .ok_or(Error::Native(format!("trying to get native global with index {}", index))) } fn function_type(&self, function_index: ItemIndex) -> Result { @@ -139,7 +177,7 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { ItemIndex::External(_) => unreachable!("trying to call function, exported by native env module"), }; - if index < NATIVE_INDEX_FUNC_MIN { + if index < NATIVE_INDEX_FUNC_MIN || index >= NATIVE_INDEX_GLOBAL_MIN { return self.env.function_type(function_index); } @@ -165,20 +203,23 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { } fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result, Error> { - if index < NATIVE_INDEX_FUNC_MIN { + if index < NATIVE_INDEX_FUNC_MIN || index >= NATIVE_INDEX_GLOBAL_MIN { return self.env.call_internal_function(outer, index); } self.functions .get((index - NATIVE_INDEX_FUNC_MIN) as usize) .ok_or(Error::Native(format!("trying to call native function with index {}", index))) - .and_then(|f| self.executor.write().execute(&f.name(), outer)) + .and_then(|f| self.executor.write() + .as_mut() + .expect("function existss; if function exists, executor must also exists [checked in constructor]; qed") + .execute(&f.name(), outer)) } } /// Create wrapper for env module with given native user functions. -pub fn env_native_module<'a>(env: Arc, user_functions: UserFunctions<'a>) -> Result { - NativeModuleInstance::new(env, user_functions) +pub fn env_native_module<'a>(env: Arc, user_elements: UserDefinedElements<'a>) -> Result { + NativeModuleInstance::new(env, user_elements) } impl<'a> PartialEq for UserFunctionDescriptor { diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs index 1f7912b..7243e27 100644 --- a/src/interpreter/imports.rs +++ b/src/interpreter/imports.rs @@ -151,7 +151,7 @@ impl ModuleImports { pub fn global<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry, required_type: Option) -> Result, Error> { let (module, export) = self.external_export(externals, import, &required_type.clone().map(|rt| ExportEntryType::Global(rt)).unwrap_or(ExportEntryType::Any))?; if let Internal::Global(external_index) = export { - return module.global(ItemIndex::Internal(external_index), required_type); + return module.global(ItemIndex::Internal(external_index), required_type, externals); } Err(Error::Program(format!("wrong import {} from module {} (expecting global)", import.field(), import.module()))) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1d59c06..3ebeccd 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -78,6 +78,6 @@ pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, Expor pub use self::table::TableInstance; pub use self::program::ProgramInstance; pub use self::value::RuntimeValue; -pub use self::variable::VariableInstance; -pub use self::env_native::{env_native_module, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor}; +pub use self::variable::{VariableInstance, VariableType, ExternalVariableValue}; +pub use self::env_native::{env_native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; pub use self::env::EnvParams; \ No newline at end of file diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 97264cf..9327154 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -62,7 +62,7 @@ pub trait ModuleInstanceInterface { /// Get memory reference. fn memory(&self, index: ItemIndex) -> Result, Error>; /// Get global reference. - fn global(&self, index: ItemIndex, variable_type: Option) -> Result, Error>; + fn global<'a>(&self, index: ItemIndex, variable_type: Option, externals: Option<&'a HashMap>>) -> Result, Error>; /// Get function type for given function index. fn function_type(&self, function_index: ItemIndex) -> Result; /// Get function type for given function index. @@ -241,7 +241,7 @@ impl ModuleInstance { self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index)); }, &Internal::Global(global_index) => { - self.global(ItemIndex::IndexSpace(global_index), None) + self.global(ItemIndex::IndexSpace(global_index), None, externals) .and_then(|g| if g.is_mutable() { Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) } else { @@ -337,6 +337,7 @@ impl ModuleInstance { let mut context = FunctionValidationContext::new( self, + externals, &locals, DEFAULT_VALUE_STACK_LIMIT, DEFAULT_FRAME_STACK_LIMIT, @@ -430,7 +431,7 @@ impl ModuleInstanceInterface for ModuleInstance { fn execute_export(&self, name: &str, params: ExecutionParams) -> Result, Error> { let index = self.exports.get(name) - .ok_or(Error::Function(format!("missing exports with name {}", name))) + .ok_or(Error::Function(format!("missing executable export with name {}", name))) .and_then(|l| l.iter() .find(|i| match i { &&Internal::Function(_) => true, @@ -447,12 +448,12 @@ impl ModuleInstanceInterface for ModuleInstance { fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result { self.exports.get(name) - .ok_or(Error::Function(format!("missing exports with name {}", name))) + .ok_or(Error::Function(format!("missing export entry with name {}", name))) .and_then(|l| l.iter() .find(|i| match required_type { &ExportEntryType::Any => true, &ExportEntryType::Global(global_type) => match i { - &&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false), + &&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type), None).map(|_| true).unwrap_or(false), _ => false, }, &ExportEntryType::Function(ref required_type) => match i { @@ -493,7 +494,7 @@ impl ModuleInstanceInterface for ModuleInstance { } } - fn global(&self, index: ItemIndex, variable_type: Option) -> Result, Error> { + fn global<'a>(&self, index: ItemIndex, variable_type: Option, externals: Option<&'a HashMap>>) -> Result, Error> { match self.imports.parse_global_index(index) { ItemIndex::IndexSpace(_) => unreachable!("parse_global_index resolves IndexSpace option"), ItemIndex::Internal(index) => self.globals.get(index as usize).cloned() @@ -502,7 +503,7 @@ impl ModuleInstanceInterface for ModuleInstance { .ok_or(Error::Global(format!("trying to access 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 access external global with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| self.imports.global(None, e, variable_type)), + .and_then(|e| self.imports.global(externals, e, variable_type)), } } diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index 429a214..906e48f 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -477,7 +477,7 @@ impl Interpreter { fn run_get_global<'a>(context: &mut FunctionContext, index: u32) -> Result, Error> { context.module() - .global(ItemIndex::IndexSpace(index), None) + .global(ItemIndex::IndexSpace(index), None, Some(context.externals)) .and_then(|g| context.value_stack_mut().push(g.get())) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -486,7 +486,7 @@ impl Interpreter { context .value_stack_mut() .pop() - .and_then(|v| context.module().global(ItemIndex::IndexSpace(index), None).and_then(|g| g.set(v))) + .and_then(|v| context.module().global(ItemIndex::IndexSpace(index), None, Some(context.externals)).and_then(|g| g.set(v))) .map(|_| InstructionOutcome::RunNextInstruction) } diff --git a/src/interpreter/table.rs b/src/interpreter/table.rs index 98c7b52..37b0255 100644 --- a/src/interpreter/table.rs +++ b/src/interpreter/table.rs @@ -12,7 +12,12 @@ pub struct TableInstance { /// Table variables type. variable_type: VariableType, /// Table memory buffer. - buffer: RwLock>, + buffer: RwLock>, +} + +/// Table element. Cloneable wrapper around VariableInstance. +struct TableElement { + pub var: VariableInstance, } impl TableInstance { @@ -24,7 +29,7 @@ impl TableInstance { Ok(Arc::new(TableInstance { variable_type: variable_type, buffer: RwLock::new( - vec![VariableInstance::new(true, variable_type, RuntimeValue::Null)?; table_type.limits().initial() as usize] + vec![TableElement::new(VariableInstance::new(true, variable_type, RuntimeValue::Null)?); table_type.limits().initial() as usize] ), })) } @@ -39,7 +44,7 @@ impl TableInstance { let buffer = self.buffer.read(); let buffer_len = buffer.len(); buffer.get(offset as usize) - .map(|v| v.get()) + .map(|v| v.var.get()) .ok_or(Error::Table(format!("trying to read table item with index {} when there are only {} items", offset, buffer_len))) } @@ -61,6 +66,21 @@ impl TableInstance { let buffer_len = buffer.len(); buffer.get_mut(offset as usize) .ok_or(Error::Table(format!("trying to update table item with index {} when there are only {} items", offset, buffer_len))) - .and_then(|v| v.set(value)) + .and_then(|v| v.var.set(value)) + } +} + +impl TableElement { + pub fn new(var: VariableInstance) -> Self { + TableElement { + var: var, + } + } +} + +impl Clone for TableElement { + fn clone(&self) -> Self { + TableElement::new(VariableInstance::new(self.var.is_mutable(), self.var.variable_type(), self.var.get()) + .expect("it only fails when variable_type() != passed variable value; both are read from already constructed var; qed")) } } diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index 87b0fb2..30f79c4 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -1,16 +1,18 @@ ///! Basic tests for instructions/constructions, missing in wabt tests use std::sync::{Arc, Weak}; +use std::collections::HashMap; use builder::module; use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType}; use interpreter::Error; -use interpreter::env_native::{env_native_module, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor}; +use interpreter::env_native::{env_native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; use interpreter::memory::MemoryInstance; use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType, FunctionSignature}; use interpreter::program::ProgramInstance; use interpreter::validator::{FunctionValidationContext, Validator}; -use interpreter::value::RuntimeValue; +use interpreter::value::{RuntimeValue, TryInto}; +use interpreter::variable::{VariableInstance, ExternalVariableValue, VariableType}; #[test] fn import_function() { @@ -115,6 +117,24 @@ const SIGNATURES: &'static [UserFunctionDescriptor] = &[ ), ]; +const NO_SIGNATURES: &'static [UserFunctionDescriptor] = &[]; + +// 'external' variable +struct MeasuredVariable { + pub val: i32, +} + +impl ExternalVariableValue for MeasuredVariable { + fn get(&self) -> RuntimeValue { + RuntimeValue::I32(self.val) + } + + fn set(&mut self, val: RuntimeValue) -> Result<(), Error> { + self.val = val.try_into()?; + Ok(()) + } +} + // user function executor struct FunctionExecutor { pub memory: Arc, @@ -152,7 +172,7 @@ impl UserFunctionExecutor for FunctionExecutor { } #[test] -fn single_program_different_modules() { +fn native_env_function() { // create new program let program = ProgramInstance::new().unwrap(); // => env module is created @@ -166,8 +186,9 @@ fn single_program_different_modules() { values: Vec::new(), }; { - let functions: UserFunctions = UserFunctions { - executor: &mut executor, + let functions: UserDefinedElements = UserDefinedElements { + executor: Some(&mut executor), + globals: HashMap::new(), functions: ::std::borrow::Cow::from(SIGNATURES), }; let native_env_instance = Arc::new(env_native_module(env_instance, functions).unwrap()); @@ -210,6 +231,57 @@ fn single_program_different_modules() { assert_eq!(executor.values, vec![7, 57, 42]); } +#[test] +fn native_env_global() { + // module constructor + let module_constructor = |elements| { + let program = ProgramInstance::new().unwrap(); + let env_instance = program.module("env").unwrap(); + let native_env_instance = Arc::new(env_native_module(env_instance.clone(), elements).unwrap()); + let params = ExecutionParams::with_external("env".into(), native_env_instance); + + let module = module() + .with_import(ImportEntry::new("env".into(), "ext_global".into(), External::Global(GlobalType::new(ValueType::I32, false)))) + .function() + .signature().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::GetGlobal(0), + Opcode::End, + ])).build() + .build() + .build(); + program.add_module("main", module, Some(¶ms.externals))? + .execute_index(0, params.clone()) + }; + + // try to add module, exporting non-existant env' variable => error + { + assert!(module_constructor(UserDefinedElements { + executor: None, + globals: HashMap::new(), + functions: ::std::borrow::Cow::from(NO_SIGNATURES), + }).is_err()); + } + + // now add simple variable natively => ok + { + assert_eq!(module_constructor(UserDefinedElements { + executor: None, + globals: vec![("ext_global".into(), Arc::new(VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(777)).unwrap()))].into_iter().collect(), + functions: ::std::borrow::Cow::from(NO_SIGNATURES), + }).unwrap().unwrap(), RuntimeValue::I32(777)); + } + + // now add 'getter+setter' variable natively => ok + { + assert_eq!(module_constructor(UserDefinedElements { + executor: None, + globals: vec![("ext_global".into(), Arc::new(VariableInstance::new_external_global(false, VariableType::I32, Box::new(MeasuredVariable { val: 345 })).unwrap()))].into_iter().collect(), + functions: ::std::borrow::Cow::from(NO_SIGNATURES), + }).unwrap().unwrap(), RuntimeValue::I32(345)); + } +} + #[test] fn import_env_mutable_global() { let program = ProgramInstance::new().unwrap(); @@ -228,8 +300,9 @@ fn env_native_export_entry_type_check() { memory: program.module("env").unwrap().memory(ItemIndex::Internal(0)).unwrap(), values: Vec::new(), }; - let native_env_instance = Arc::new(env_native_module(program.module("env").unwrap(), UserFunctions { - executor: &mut function_executor, + let native_env_instance = Arc::new(env_native_module(program.module("env").unwrap(), UserDefinedElements { + executor: Some(&mut function_executor), + globals: HashMap::new(), functions: ::std::borrow::Cow::from(SIGNATURES), }).unwrap()); @@ -242,7 +315,7 @@ fn env_native_export_entry_type_check() { #[test] fn if_else_with_return_type_validation() { let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); - let mut context = FunctionValidationContext::new(&module_instance, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default())); + let mut context = FunctionValidationContext::new(&module_instance, None, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default())); Validator::validate_function(&mut context, BlockType::NoResult, &[ Opcode::I32Const(1), diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs index b374e6b..43f411b 100644 --- a/src/interpreter/validator.rs +++ b/src/interpreter/validator.rs @@ -1,4 +1,5 @@ use std::u32; +use std::sync::Arc; use std::collections::HashMap; use elements::{Opcode, BlockType, ValueType}; use interpreter::Error; @@ -14,6 +15,8 @@ const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF; pub struct FunctionValidationContext<'a> { /// Wasm module instance (in process of instantiation). module_instance: &'a ModuleInstance, + /// Native externals. + externals: Option<&'a HashMap>>, /// Current instruction position. position: usize, /// Local variables. @@ -569,6 +572,7 @@ impl Validator { impl<'a> FunctionValidationContext<'a> { pub fn new( module_instance: &'a ModuleInstance, + externals: Option<&'a HashMap>>, locals: &'a [ValueType], value_stack_limit: usize, frame_stack_limit: usize, @@ -576,6 +580,7 @@ impl<'a> FunctionValidationContext<'a> { ) -> Self { FunctionValidationContext { module_instance: module_instance, + externals: externals, position: 0, locals: locals, value_stack: StackWithLimit::with_limit(value_stack_limit), @@ -688,7 +693,7 @@ impl<'a> FunctionValidationContext<'a> { pub fn require_global(&self, idx: u32, mutability: Option) -> Result { self.module_instance - .global(ItemIndex::IndexSpace(idx), None) + .global(ItemIndex::IndexSpace(idx), None, self.externals.clone()) .and_then(|g| match mutability { Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be mutable", idx))), Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be immutable", idx))), diff --git a/src/interpreter/variable.rs b/src/interpreter/variable.rs index e96f804..50767ba 100644 --- a/src/interpreter/variable.rs +++ b/src/interpreter/variable.rs @@ -1,3 +1,4 @@ +use std::fmt; use parking_lot::RwLock; use elements::{GlobalType, ValueType, TableElementType}; use interpreter::Error; @@ -18,6 +19,14 @@ pub enum VariableType { F64, } +/// Externally stored variable value. +pub trait ExternalVariableValue { + /// Get variable value. + fn get(&self) -> RuntimeValue; + /// Set variable value. + fn set(&mut self, value: RuntimeValue) -> Result<(), Error>; +} + /// Variable instance. #[derive(Debug)] pub struct VariableInstance { @@ -26,7 +35,15 @@ pub struct VariableInstance { /// Variable type. variable_type: VariableType, /// Global value. - value: RwLock, + value: RwLock, +} + +/// Enum variable value. +enum VariableValue { + /// Internal value. + Internal(RuntimeValue), + /// External value. + External(Box), } impl VariableInstance { @@ -40,7 +57,7 @@ impl VariableInstance { Ok(VariableInstance { is_mutable: is_mutable, variable_type: variable_type, - value: RwLock::new(value), + value: RwLock::new(VariableValue::Internal(value)), }) } @@ -49,6 +66,21 @@ impl VariableInstance { Self::new(global_type.is_mutable(), global_type.content_type().into(), value) } + /// New global with externally stored value. + pub fn new_external_global(is_mutable: bool, variable_type: VariableType, value: Box) -> Result { + // TODO: there is nothing about null value in specification + there is nothing about initializing missing table elements? => runtime check for nulls + let current_value = value.get(); + if !current_value.is_null() && current_value.variable_type() != Some(variable_type) { + return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, current_value.variable_type()))); + } + + Ok(VariableInstance { + is_mutable: is_mutable, + variable_type: variable_type, + value: RwLock::new(VariableValue::External(value)), + }) + } + /// Is mutable pub fn is_mutable(&self) -> bool { self.is_mutable @@ -61,7 +93,7 @@ impl VariableInstance { /// Get the value of the variable instance pub fn get(&self) -> RuntimeValue { - self.value.read().clone() + self.value.read().get() } /// Set the value of the variable instance @@ -73,17 +105,25 @@ impl VariableInstance { return Err(Error::Variable(format!("trying to update variable of type {:?} with value of type {:?}", self.variable_type, value.variable_type()))); } - *self.value.write() = value; - Ok(()) + self.value.write().set(value) } } -impl Clone for VariableInstance { - fn clone(&self) -> Self { - VariableInstance { - is_mutable: self.is_mutable, - variable_type: self.variable_type, - value: RwLock::new(self.value.read().clone()), +impl VariableValue { + fn get(&self) -> RuntimeValue { + match *self { + VariableValue::Internal(ref value) => value.clone(), + VariableValue::External(ref value) => value.get(), + } + } + + fn set(&mut self, new_value: RuntimeValue) -> Result<(), Error> { + match *self { + VariableValue::Internal(ref mut value) => { + *value = new_value; + Ok(()) + }, + VariableValue::External(ref mut value) => value.set(new_value), } } } @@ -106,3 +146,12 @@ impl From for VariableType { } } } + +impl fmt::Debug for VariableValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + VariableValue::Internal(ref value) => write!(f, "Variable.Internal({:?})", value), + VariableValue::External(ref value) => write!(f, "Variable.External({:?})", value.get()), + } + } +}