mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-23 11:41:58 +00:00
native globals
This commit is contained in:
@ -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())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())),
|
||||
|
@ -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 {
|
||||
|
@ -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())))
|
||||
|
@ -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;
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
@ -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(¶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),
|
||||
|
@ -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))),
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user