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)), elements::Internal::Global(global_index) => Ok(ItemIndex::IndexSpace(global_index)),
_ => Err(InterpreterError::Global(format!("Expected to have exported global with name {}", field))), _ => 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) self.instance.memory(index)
} }
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> {
self.instance.global(index, variable_type) self.instance.global(index, variable_type, externals)
} }
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionSignature, Error> { 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> { 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 // to make interpreter independent of *SCRIPTEN runtime, just make abort/assert = interpreter Error
match index { 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(|g| g.set(RuntimeValue::I32(1)))
.and_then(|_| Err(Error::Trap("abort".into()))), .and_then(|_| Err(Error::Trap("abort".into()))),
INDEX_FUNC_ASSERT => outer.value_stack.pop_as::<i32>() INDEX_FUNC_ASSERT => outer.value_stack.pop_as::<i32>()
.and_then(|condition| if condition == 0 { .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(|g| g.set(RuntimeValue::I32(1)))
.and_then(|_| Err(Error::Trap("assertion failed".into()))) .and_then(|_| Err(Error::Trap("assertion failed".into())))
} else { } else {
Ok(None) Ok(None)
}), }),
INDEX_FUNC_ENLARGE_MEMORY => Ok(Some(RuntimeValue::I32(0))), // TODO: support memory enlarge 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(|g| g.get())
.map(Some), .map(Some),
INDEX_FUNC_MIN_NONUSED ... INDEX_FUNC_MAX => Err(Error::Trap("unimplemented".into())), 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. /// Min index of native function.
pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001; pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001;
/// Min index of native global.
pub const NATIVE_INDEX_GLOBAL_MIN: u32 = 20001;
/// User functions executor. /// User functions executor.
pub trait UserFunctionExecutor { pub trait UserFunctionExecutor {
@ -65,12 +67,14 @@ impl UserFunctionDescriptor {
} }
} }
/// Set of user-defined functions /// Set of user-defined module elements.
pub struct UserFunctions<'a> { pub struct UserDefinedElements<'a> {
/// Functions list. /// User globals list.
pub globals: HashMap<String, Arc<VariableInstance>>,
/// User functions list.
pub functions: Cow<'static, [UserFunctionDescriptor]>, pub functions: Cow<'static, [UserFunctionDescriptor]>,
/// Functions executor. /// Functions executor.
pub executor: &'a mut UserFunctionExecutor, pub executor: Option<&'a mut UserFunctionExecutor>,
} }
/// Native module instance. /// Native module instance.
@ -78,21 +82,31 @@ pub struct NativeModuleInstance<'a> {
/// Underllying module reference. /// Underllying module reference.
env: Arc<ModuleInstanceInterface>, env: Arc<ModuleInstanceInterface>,
/// User function executor. /// User function executor.
executor: RwLock<&'a mut UserFunctionExecutor>, executor: RwLock<Option<&'a mut UserFunctionExecutor>>,
/// By-name functions index. /// By-name functions index.
by_name: HashMap<String, u32>, functions_by_name: HashMap<String, u32>,
/// User functions list. /// User functions list.
functions: Cow<'static, [UserFunctionDescriptor]>, 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> { impl<'a> NativeModuleInstance<'a> {
/// Create new native module /// 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 { Ok(NativeModuleInstance {
env: env, env: env,
executor: RwLock::new(functions.executor), executor: RwLock::new(elements.executor),
by_name: functions.functions.iter().enumerate().map(|(i, f)| (f.name().to_owned(), i as u32)).collect(), functions_by_name: elements.functions.iter().enumerate().map(|(i, f)| (f.name().to_owned(), i as u32)).collect(),
functions: functions.functions, 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> { 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; let composite_index = NATIVE_INDEX_FUNC_MIN + *index;
match required_type { match required_type {
&ExportEntryType::Any => return Ok(Internal::Function(composite_index)),
&ExportEntryType::Function(ref required_type) &ExportEntryType::Function(ref required_type)
if self.function_type(ItemIndex::Internal(composite_index)) 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 .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) self.env.export_entry(name, required_type)
} }
@ -129,8 +155,20 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> {
self.env.memory(index) self.env.memory(index)
} }
fn global(&self, index: ItemIndex, variable_type: Option<VariableType>) -> Result<Arc<VariableInstance>, Error> { fn global<'b>(&self, global_index: ItemIndex, variable_type: Option<VariableType>, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<Arc<VariableInstance>, Error> {
self.env.global(index, variable_type) 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> { 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"), 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); 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> { 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); return self.env.call_internal_function(outer, index);
} }
self.functions self.functions
.get((index - NATIVE_INDEX_FUNC_MIN) as usize) .get((index - NATIVE_INDEX_FUNC_MIN) as usize)
.ok_or(Error::Native(format!("trying to call native function with index {}", index))) .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. /// 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> { pub fn env_native_module<'a>(env: Arc<ModuleInstanceInterface>, user_elements: UserDefinedElements<'a>) -> Result<NativeModuleInstance, Error> {
NativeModuleInstance::new(env, user_functions) NativeModuleInstance::new(env, user_elements)
} }
impl<'a> PartialEq for UserFunctionDescriptor { 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> { 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))?; 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 { 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()))) 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::table::TableInstance;
pub use self::program::ProgramInstance; pub use self::program::ProgramInstance;
pub use self::value::RuntimeValue; pub use self::value::RuntimeValue;
pub use self::variable::VariableInstance; pub use self::variable::{VariableInstance, VariableType, ExternalVariableValue};
pub use self::env_native::{env_native_module, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor}; pub use self::env_native::{env_native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor};
pub use self::env::EnvParams; pub use self::env::EnvParams;

View File

@ -62,7 +62,7 @@ pub trait ModuleInstanceInterface {
/// Get memory reference. /// Get memory reference.
fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error>; fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error>;
/// Get global reference. /// 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. /// Get function type for given function index.
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionSignature, Error>; fn function_type(&self, function_index: ItemIndex) -> Result<FunctionSignature, Error>;
/// Get function type for given function index. /// 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)); self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index));
}, },
&Internal::Global(global_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() { .and_then(|g| if g.is_mutable() {
Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) Err(Error::Validation(format!("trying to export mutable global {}", export.field())))
} else { } else {
@ -337,6 +337,7 @@ impl ModuleInstance {
let mut context = FunctionValidationContext::new( let mut context = FunctionValidationContext::new(
self, self,
externals,
&locals, &locals,
DEFAULT_VALUE_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_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> { fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
let index = self.exports.get(name) 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() .and_then(|l| l.iter()
.find(|i| match i { .find(|i| match i {
&&Internal::Function(_) => true, &&Internal::Function(_) => true,
@ -447,12 +448,12 @@ impl ModuleInstanceInterface for ModuleInstance {
fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error> { fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error> {
self.exports.get(name) 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() .and_then(|l| l.iter()
.find(|i| match required_type { .find(|i| match required_type {
&ExportEntryType::Any => true, &ExportEntryType::Any => true,
&ExportEntryType::Global(global_type) => match i { &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, _ => false,
}, },
&ExportEntryType::Function(ref required_type) => match i { &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) { match self.imports.parse_global_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_global_index resolves IndexSpace option"), ItemIndex::IndexSpace(_) => unreachable!("parse_global_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.globals.get(index as usize).cloned() 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))) .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) .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())))) .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> { fn run_get_global<'a>(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
context.module() 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())) .and_then(|g| context.value_stack_mut().push(g.get()))
.map(|_| InstructionOutcome::RunNextInstruction) .map(|_| InstructionOutcome::RunNextInstruction)
} }
@ -486,7 +486,7 @@ impl Interpreter {
context context
.value_stack_mut() .value_stack_mut()
.pop() .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) .map(|_| InstructionOutcome::RunNextInstruction)
} }

View File

@ -12,7 +12,12 @@ pub struct TableInstance {
/// Table variables type. /// Table variables type.
variable_type: VariableType, variable_type: VariableType,
/// Table memory buffer. /// Table memory buffer.
buffer: RwLock<Vec<VariableInstance>>, buffer: RwLock<Vec<TableElement>>,
}
/// Table element. Cloneable wrapper around VariableInstance.
struct TableElement {
pub var: VariableInstance,
} }
impl TableInstance { impl TableInstance {
@ -24,7 +29,7 @@ impl TableInstance {
Ok(Arc::new(TableInstance { Ok(Arc::new(TableInstance {
variable_type: variable_type, variable_type: variable_type,
buffer: RwLock::new( 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 = self.buffer.read();
let buffer_len = buffer.len(); let buffer_len = buffer.len();
buffer.get(offset as usize) 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))) .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(); let buffer_len = buffer.len();
buffer.get_mut(offset as usize) 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))) .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 ///! Basic tests for instructions/constructions, missing in wabt tests
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::collections::HashMap;
use builder::module; use builder::module;
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType}; InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType};
use interpreter::Error; 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::memory::MemoryInstance;
use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType, FunctionSignature}; use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType, FunctionSignature};
use interpreter::program::ProgramInstance; use interpreter::program::ProgramInstance;
use interpreter::validator::{FunctionValidationContext, Validator}; use interpreter::validator::{FunctionValidationContext, Validator};
use interpreter::value::RuntimeValue; use interpreter::value::{RuntimeValue, TryInto};
use interpreter::variable::{VariableInstance, ExternalVariableValue, VariableType};
#[test] #[test]
fn import_function() { 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 // user function executor
struct FunctionExecutor { struct FunctionExecutor {
pub memory: Arc<MemoryInstance>, pub memory: Arc<MemoryInstance>,
@ -152,7 +172,7 @@ impl UserFunctionExecutor for FunctionExecutor {
} }
#[test] #[test]
fn single_program_different_modules() { fn native_env_function() {
// create new program // create new program
let program = ProgramInstance::new().unwrap(); let program = ProgramInstance::new().unwrap();
// => env module is created // => env module is created
@ -166,8 +186,9 @@ fn single_program_different_modules() {
values: Vec::new(), values: Vec::new(),
}; };
{ {
let functions: UserFunctions = UserFunctions { let functions: UserDefinedElements = UserDefinedElements {
executor: &mut executor, executor: Some(&mut executor),
globals: HashMap::new(),
functions: ::std::borrow::Cow::from(SIGNATURES), functions: ::std::borrow::Cow::from(SIGNATURES),
}; };
let native_env_instance = Arc::new(env_native_module(env_instance, functions).unwrap()); 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]); 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] #[test]
fn import_env_mutable_global() { fn import_env_mutable_global() {
let program = ProgramInstance::new().unwrap(); 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(), memory: program.module("env").unwrap().memory(ItemIndex::Internal(0)).unwrap(),
values: Vec::new(), values: Vec::new(),
}; };
let native_env_instance = Arc::new(env_native_module(program.module("env").unwrap(), UserFunctions { let native_env_instance = Arc::new(env_native_module(program.module("env").unwrap(), UserDefinedElements {
executor: &mut function_executor, executor: Some(&mut function_executor),
globals: HashMap::new(),
functions: ::std::borrow::Cow::from(SIGNATURES), functions: ::std::borrow::Cow::from(SIGNATURES),
}).unwrap()); }).unwrap());
@ -242,7 +315,7 @@ fn env_native_export_entry_type_check() {
#[test] #[test]
fn if_else_with_return_type_validation() { fn if_else_with_return_type_validation() {
let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); 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, &[ Validator::validate_function(&mut context, BlockType::NoResult, &[
Opcode::I32Const(1), Opcode::I32Const(1),

View File

@ -1,4 +1,5 @@
use std::u32; use std::u32;
use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
use elements::{Opcode, BlockType, ValueType}; use elements::{Opcode, BlockType, ValueType};
use interpreter::Error; use interpreter::Error;
@ -14,6 +15,8 @@ const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
pub struct FunctionValidationContext<'a> { pub struct FunctionValidationContext<'a> {
/// Wasm module instance (in process of instantiation). /// Wasm module instance (in process of instantiation).
module_instance: &'a ModuleInstance, module_instance: &'a ModuleInstance,
/// Native externals.
externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>,
/// Current instruction position. /// Current instruction position.
position: usize, position: usize,
/// Local variables. /// Local variables.
@ -569,6 +572,7 @@ impl Validator {
impl<'a> FunctionValidationContext<'a> { impl<'a> FunctionValidationContext<'a> {
pub fn new( pub fn new(
module_instance: &'a ModuleInstance, module_instance: &'a ModuleInstance,
externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>,
locals: &'a [ValueType], locals: &'a [ValueType],
value_stack_limit: usize, value_stack_limit: usize,
frame_stack_limit: usize, frame_stack_limit: usize,
@ -576,6 +580,7 @@ impl<'a> FunctionValidationContext<'a> {
) -> Self { ) -> Self {
FunctionValidationContext { FunctionValidationContext {
module_instance: module_instance, module_instance: module_instance,
externals: externals,
position: 0, position: 0,
locals: locals, locals: locals,
value_stack: StackWithLimit::with_limit(value_stack_limit), 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> { pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<StackValueType, Error> {
self.module_instance self.module_instance
.global(ItemIndex::IndexSpace(idx), None) .global(ItemIndex::IndexSpace(idx), None, self.externals.clone())
.and_then(|g| match mutability { .and_then(|g| match mutability {
Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be mutable", idx))), 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))), 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 parking_lot::RwLock;
use elements::{GlobalType, ValueType, TableElementType}; use elements::{GlobalType, ValueType, TableElementType};
use interpreter::Error; use interpreter::Error;
@ -18,6 +19,14 @@ pub enum VariableType {
F64, 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. /// Variable instance.
#[derive(Debug)] #[derive(Debug)]
pub struct VariableInstance { pub struct VariableInstance {
@ -26,7 +35,15 @@ pub struct VariableInstance {
/// Variable type. /// Variable type.
variable_type: VariableType, variable_type: VariableType,
/// Global value. /// Global value.
value: RwLock<RuntimeValue>, value: RwLock<VariableValue>,
}
/// Enum variable value.
enum VariableValue {
/// Internal value.
Internal(RuntimeValue),
/// External value.
External(Box<ExternalVariableValue>),
} }
impl VariableInstance { impl VariableInstance {
@ -40,7 +57,7 @@ impl VariableInstance {
Ok(VariableInstance { Ok(VariableInstance {
is_mutable: is_mutable, is_mutable: is_mutable,
variable_type: variable_type, 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) 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 /// Is mutable
pub fn is_mutable(&self) -> bool { pub fn is_mutable(&self) -> bool {
self.is_mutable self.is_mutable
@ -61,7 +93,7 @@ impl VariableInstance {
/// Get the value of the variable instance /// Get the value of the variable instance
pub fn get(&self) -> RuntimeValue { pub fn get(&self) -> RuntimeValue {
self.value.read().clone() self.value.read().get()
} }
/// Set the value of the variable instance /// 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()))); 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; self.value.write().set(value)
Ok(())
} }
} }
impl Clone for VariableInstance { impl VariableValue {
fn clone(&self) -> Self { fn get(&self) -> RuntimeValue {
VariableInstance { match *self {
is_mutable: self.is_mutable, VariableValue::Internal(ref value) => value.clone(),
variable_type: self.variable_type, VariableValue::External(ref value) => value.get(),
value: RwLock::new(self.value.read().clone()), }
}
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()),
}
}
}