native globals

This commit is contained in:
Svyatoslav Nikolsky
2017-07-31 11:58:24 +03:00
parent 01aa70463d
commit 8eee595e77
11 changed files with 249 additions and 60 deletions

View File

@ -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())))
}
}
}

View File

@ -116,8 +116,8 @@ impl ModuleInstanceInterface for EnvModuleInstance {
self.instance.memory(index)
}
fn global(&self, index: ItemIndex, variable_type: Option<VariableType>) -> Result<Arc<VariableInstance>, Error> {
self.instance.global(index, variable_type)
fn global<'a>(&self, index: ItemIndex, variable_type: Option<VariableType>, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<VariableInstance>, Error> {
self.instance.global(index, variable_type, externals)
}
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionSignature, Error> {
@ -143,19 +143,19 @@ impl ModuleInstanceInterface for EnvModuleInstance {
fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, 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::<i32>()
.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())),

View File

@ -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<String, Arc<VariableInstance>>,
/// 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<ModuleInstanceInterface>,
/// User function executor.
executor: RwLock<&'a mut UserFunctionExecutor>,
executor: RwLock<Option<&'a mut UserFunctionExecutor>>,
/// By-name functions index.
by_name: HashMap<String, u32>,
functions_by_name: HashMap<String, u32>,
/// User functions list.
functions: Cow<'static, [UserFunctionDescriptor]>,
/// By-name functions index.
globals_by_name: HashMap<String, u32>,
/// User globals list.
globals: Vec<Arc<VariableInstance>>,
}
impl<'a> NativeModuleInstance<'a> {
/// Create new native module
pub fn new(env: Arc<ModuleInstanceInterface>, functions: UserFunctions<'a>) -> Result<Self, Error> {
pub fn new(env: Arc<ModuleInstanceInterface>, elements: UserDefinedElements<'a>) -> Result<Self, Error> {
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<Internal, Error> {
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<VariableType>) -> Result<Arc<VariableInstance>, Error> {
self.env.global(index, variable_type)
fn global<'b>(&self, global_index: ItemIndex, variable_type: Option<VariableType>, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<Arc<VariableInstance>, 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<FunctionSignature, Error> {
@ -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<Option<RuntimeValue>, 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<ModuleInstanceInterface>, user_functions: UserFunctions<'a>) -> Result<NativeModuleInstance, Error> {
NativeModuleInstance::new(env, user_functions)
pub fn env_native_module<'a>(env: Arc<ModuleInstanceInterface>, user_elements: UserDefinedElements<'a>) -> Result<NativeModuleInstance, Error> {
NativeModuleInstance::new(env, user_elements)
}
impl<'a> PartialEq for UserFunctionDescriptor {

View File

@ -151,7 +151,7 @@ impl ModuleImports {
pub fn global<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry, required_type: Option<VariableType>) -> Result<Arc<VariableInstance>, 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())))

View File

@ -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;

View File

@ -62,7 +62,7 @@ pub trait ModuleInstanceInterface {
/// Get memory reference.
fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error>;
/// Get global reference.
fn global(&self, index: ItemIndex, variable_type: Option<VariableType>) -> Result<Arc<VariableInstance>, Error>;
fn global<'a>(&self, index: ItemIndex, variable_type: Option<VariableType>, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<VariableInstance>, Error>;
/// Get function type for given function index.
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionSignature, Error>;
/// 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<Option<RuntimeValue>, 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<Internal, Error> {
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<VariableType>) -> Result<Arc<VariableInstance>, Error> {
fn global<'a>(&self, index: ItemIndex, variable_type: Option<VariableType>, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<VariableInstance>, 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)),
}
}

View File

@ -477,7 +477,7 @@ impl Interpreter {
fn run_get_global<'a>(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome<'a>, 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)
}

View File

@ -12,7 +12,12 @@ pub struct TableInstance {
/// Table variables type.
variable_type: VariableType,
/// Table memory buffer.
buffer: RwLock<Vec<VariableInstance>>,
buffer: RwLock<Vec<TableElement>>,
}
/// 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"))
}
}

View File

@ -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<MemoryInstance>,
@ -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(&params.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),

View File

@ -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<String, Arc<ModuleInstanceInterface + 'a>>>,
/// 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<String, Arc<ModuleInstanceInterface + 'a>>>,
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<bool>) -> Result<StackValueType, Error> {
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))),

View File

@ -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<RuntimeValue>,
value: RwLock<VariableValue>,
}
/// Enum variable value.
enum VariableValue {
/// Internal value.
Internal(RuntimeValue),
/// External value.
External(Box<ExternalVariableValue>),
}
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<ExternalVariableValue>) -> Result<Self, Error> {
// 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<TableElementType> 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()),
}
}
}