mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-23 19:52:02 +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)),
|
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())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())),
|
||||||
|
@ -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 {
|
||||||
|
@ -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())))
|
||||||
|
@ -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;
|
@ -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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(¶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]
|
#[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),
|
||||||
|
@ -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))),
|
||||||
|
@ -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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user