Move mutable runtime global state to Imports.
This commit is contained in:
parent
71ad158b6a
commit
09e6b21729
259
src/engine.rs
259
src/engine.rs
@ -13,8 +13,8 @@ use crate::packages::{Package, StandardPackage};
|
|||||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
|
Dynamic, EvalAltResult, Identifier, ImmutableString, Locked, Module, Position, RhaiResult,
|
||||||
Shared, StaticVec, INT,
|
Scope, Shared, StaticVec, INT,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -40,7 +40,7 @@ use crate::ast::FnCallHashes;
|
|||||||
|
|
||||||
pub type Precedence = NonZeroU8;
|
pub type Precedence = NonZeroU8;
|
||||||
|
|
||||||
/// _(internals)_ A stack of imported [modules][Module].
|
/// _(internals)_ A stack of imported [modules][Module] plus mutable runtime global states.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// # Volatile Data Structure
|
/// # Volatile Data Structure
|
||||||
@ -49,17 +49,34 @@ pub type Precedence = NonZeroU8;
|
|||||||
//
|
//
|
||||||
// # Implementation Notes
|
// # Implementation Notes
|
||||||
//
|
//
|
||||||
// We cannot use Cow<str> here because `eval` may load a [module][Module] and
|
|
||||||
// the module name will live beyond the AST of the eval script text.
|
|
||||||
// The best we can do is a shared reference.
|
|
||||||
//
|
|
||||||
// This implementation splits the module names from the shared modules to improve data locality.
|
// This implementation splits the module names from the shared modules to improve data locality.
|
||||||
// Most usage will be looking up a particular key from the list and then getting the module that
|
// Most usage will be looking up a particular key from the list and then getting the module that
|
||||||
// corresponds to that key.
|
// corresponds to that key.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Imports {
|
pub struct Imports {
|
||||||
|
/// Stack of module names.
|
||||||
|
//
|
||||||
|
// # Implementation Notes
|
||||||
|
//
|
||||||
|
// We cannot use Cow<str> here because `eval` may load a [module][Module] and
|
||||||
|
// the module name will live beyond the AST of the eval script text.
|
||||||
keys: StaticVec<Identifier>,
|
keys: StaticVec<Identifier>,
|
||||||
|
/// Stack of imported modules.
|
||||||
modules: StaticVec<Shared<Module>>,
|
modules: StaticVec<Shared<Module>>,
|
||||||
|
/// Source of the current context.
|
||||||
|
pub source: Option<Identifier>,
|
||||||
|
/// Number of operations performed.
|
||||||
|
pub num_operations: u64,
|
||||||
|
/// Number of modules loaded.
|
||||||
|
pub num_modules: usize,
|
||||||
|
/// Function call hashes to FN_IDX_GET and FN_IDX_SET.
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
fn_hash_indexing: (u64, u64),
|
||||||
|
/// Embedded module resolver.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
pub embedded_module_resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
|
||||||
|
/// Cache of globally-defined constants.
|
||||||
|
global_constants: Option<Shared<Locked<BTreeMap<Identifier, Dynamic>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Imports {
|
impl Imports {
|
||||||
@ -70,6 +87,14 @@ impl Imports {
|
|||||||
Self {
|
Self {
|
||||||
keys: StaticVec::new(),
|
keys: StaticVec::new(),
|
||||||
modules: StaticVec::new(),
|
modules: StaticVec::new(),
|
||||||
|
source: None,
|
||||||
|
num_operations: 0,
|
||||||
|
num_modules: 0,
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
embedded_module_resolver: None,
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
fn_hash_indexing: (0, 0),
|
||||||
|
global_constants: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the length of this stack of imported [modules][Module].
|
/// Get the length of this stack of imported [modules][Module].
|
||||||
@ -175,6 +200,68 @@ impl Imports {
|
|||||||
.rev()
|
.rev()
|
||||||
.find_map(|m| m.get_qualified_iter(id))
|
.find_map(|m| m.get_qualified_iter(id))
|
||||||
}
|
}
|
||||||
|
/// Get a mutable reference to the cache of globally-defined constants.
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn global_constants_mut<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
) -> Option<impl DerefMut<Target = BTreeMap<Identifier, Dynamic>> + 'a> {
|
||||||
|
if let Some(ref mut global_constants) = self.global_constants {
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
return Some(global_constants.borrow_mut());
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
return Some(global_constants.lock().unwrap());
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Set a constant into the cache of globally-defined constants.
|
||||||
|
pub(crate) fn set_global_constant(&mut self, name: &str, value: Dynamic) {
|
||||||
|
if self.global_constants.is_none() {
|
||||||
|
let dict: Locked<_> = BTreeMap::new().into();
|
||||||
|
self.global_constants = Some(dict.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
self.global_constants
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(name.into(), value);
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
self.global_constants
|
||||||
|
.as_mut()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(name.into(), value);
|
||||||
|
}
|
||||||
|
/// Get the pre-calculated index getter hash.
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn fn_hash_idx_get(&mut self) -> u64 {
|
||||||
|
if self.fn_hash_indexing != (0, 0) {
|
||||||
|
self.fn_hash_indexing.0
|
||||||
|
} else {
|
||||||
|
let n1 = crate::calc_fn_hash(FN_IDX_GET, 2);
|
||||||
|
let n2 = crate::calc_fn_hash(FN_IDX_SET, 3);
|
||||||
|
self.fn_hash_indexing = (n1, n2);
|
||||||
|
n1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get the pre-calculated index setter hash.
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn fn_hash_idx_set(&mut self) -> u64 {
|
||||||
|
if self.fn_hash_indexing != (0, 0) {
|
||||||
|
self.fn_hash_indexing.1
|
||||||
|
} else {
|
||||||
|
let n1 = crate::calc_fn_hash(FN_IDX_GET, 2);
|
||||||
|
let n2 = crate::calc_fn_hash(FN_IDX_SET, 3);
|
||||||
|
self.fn_hash_indexing = (n1, n2);
|
||||||
|
n2
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for Imports {
|
impl IntoIterator for Imports {
|
||||||
@ -663,8 +750,6 @@ pub type FnResolutionCache = BTreeMap<u64, Option<Box<FnResolutionCacheEntry>>>;
|
|||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EvalState {
|
pub struct EvalState {
|
||||||
/// Source of the current context.
|
|
||||||
pub source: Option<Identifier>,
|
|
||||||
/// Normally, access to variables are parsed with a relative offset into the [`Scope`] to avoid a lookup.
|
/// Normally, access to variables are parsed with a relative offset into the [`Scope`] to avoid a lookup.
|
||||||
/// In some situation, e.g. after running an `eval` statement, or after a custom syntax statement,
|
/// In some situation, e.g. after running an `eval` statement, or after a custom syntax statement,
|
||||||
/// subsequent offsets may become mis-aligned.
|
/// subsequent offsets may become mis-aligned.
|
||||||
@ -673,20 +758,8 @@ pub struct EvalState {
|
|||||||
/// Level of the current scope. The global (root) level is zero, a new block (or function call)
|
/// Level of the current scope. The global (root) level is zero, a new block (or function call)
|
||||||
/// is one level higher, and so on.
|
/// is one level higher, and so on.
|
||||||
pub scope_level: usize,
|
pub scope_level: usize,
|
||||||
/// Number of operations performed.
|
|
||||||
pub num_operations: u64,
|
|
||||||
/// Number of modules loaded.
|
|
||||||
pub num_modules: usize,
|
|
||||||
/// Stack of function resolution caches.
|
/// Stack of function resolution caches.
|
||||||
fn_resolution_caches: StaticVec<FnResolutionCache>,
|
fn_resolution_caches: StaticVec<FnResolutionCache>,
|
||||||
/// Embedded module resolver.
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
pub embedded_module_resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
|
|
||||||
/// Function call hashes to FN_IDX_GET and FN_IDX_SET.
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
fn_hash_indexing: (u64, u64),
|
|
||||||
/// Cache of globally-defined constants.
|
|
||||||
pub global_constants: BTreeMap<Identifier, Dynamic>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvalState {
|
impl EvalState {
|
||||||
@ -695,17 +768,9 @@ impl EvalState {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: None,
|
|
||||||
always_search_scope: false,
|
always_search_scope: false,
|
||||||
scope_level: 0,
|
scope_level: 0,
|
||||||
num_operations: 0,
|
|
||||||
num_modules: 0,
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
embedded_module_resolver: None,
|
|
||||||
fn_resolution_caches: StaticVec::new(),
|
fn_resolution_caches: StaticVec::new(),
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
fn_hash_indexing: (0, 0),
|
|
||||||
global_constants: BTreeMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the state currently at global (root) level?
|
/// Is the state currently at global (root) level?
|
||||||
@ -743,32 +808,6 @@ impl EvalState {
|
|||||||
.pop()
|
.pop()
|
||||||
.expect("at least one function resolution cache");
|
.expect("at least one function resolution cache");
|
||||||
}
|
}
|
||||||
/// Get the pre-calculated index getter hash.
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
#[must_use]
|
|
||||||
pub fn fn_hash_idx_get(&mut self) -> u64 {
|
|
||||||
if self.fn_hash_indexing != (0, 0) {
|
|
||||||
self.fn_hash_indexing.0
|
|
||||||
} else {
|
|
||||||
let n1 = crate::calc_fn_hash(FN_IDX_GET, 2);
|
|
||||||
let n2 = crate::calc_fn_hash(FN_IDX_SET, 3);
|
|
||||||
self.fn_hash_indexing = (n1, n2);
|
|
||||||
n1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get the pre-calculated index setter hash.
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
#[must_use]
|
|
||||||
pub fn fn_hash_idx_set(&mut self) -> u64 {
|
|
||||||
if self.fn_hash_indexing != (0, 0) {
|
|
||||||
self.fn_hash_indexing.1
|
|
||||||
} else {
|
|
||||||
let n1 = crate::calc_fn_hash(FN_IDX_GET, 2);
|
|
||||||
let n2 = crate::calc_fn_hash(FN_IDX_SET, 3);
|
|
||||||
self.fn_hash_indexing = (n1, n2);
|
|
||||||
n2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ A type containing all the limits imposed by the [`Engine`].
|
/// _(internals)_ A type containing all the limits imposed by the [`Engine`].
|
||||||
@ -861,7 +900,7 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn source(&self) -> Option<&str> {
|
pub fn source(&self) -> Option<&str> {
|
||||||
self.state.source.as_ref().map(|s| s.as_str())
|
self.mods.source.as_ref().map(|s| s.as_str())
|
||||||
}
|
}
|
||||||
/// The current [`Scope`].
|
/// The current [`Scope`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -1188,33 +1227,9 @@ impl Engine {
|
|||||||
target.set_access_mode(AccessMode::ReadOnly);
|
target.set_access_mode(AccessMode::ReadOnly);
|
||||||
Ok((target.into(), *_var_pos))
|
Ok((target.into(), *_var_pos))
|
||||||
}
|
}
|
||||||
Err(mut err) => {
|
Err(err) => Err(match *err {
|
||||||
match *err {
|
EvalAltResult::ErrorVariableNotFound(_, _) => {
|
||||||
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
|
EvalAltResult::ErrorVariableNotFound(
|
||||||
*err_name = format!(
|
|
||||||
"{}{}{}",
|
|
||||||
namespace,
|
|
||||||
Token::DoubleColon.literal_syntax(),
|
|
||||||
var_name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
Err(err.fill_position(*_var_pos))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
if namespace.len() == 1 && namespace[0].name == KEYWORD_GLOBAL {
|
|
||||||
// global::VARIABLE
|
|
||||||
return if let Some(value) = state.global_constants.get_mut(var_name) {
|
|
||||||
let mut target: Target = value.clone().into();
|
|
||||||
// Module variables are constant
|
|
||||||
target.set_access_mode(AccessMode::ReadOnly);
|
|
||||||
Ok((target.into(), *_var_pos))
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorVariableNotFound(
|
|
||||||
format!(
|
format!(
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
namespace,
|
namespace,
|
||||||
@ -1223,10 +1238,39 @@ impl Engine {
|
|||||||
),
|
),
|
||||||
namespace[0].pos,
|
namespace[0].pos,
|
||||||
)
|
)
|
||||||
.into())
|
.into()
|
||||||
|
}
|
||||||
|
_ => err.fill_position(*_var_pos),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
if namespace.len() == 1 && namespace[0].name == KEYWORD_GLOBAL {
|
||||||
|
// global::VARIABLE
|
||||||
|
let global_constants = mods.global_constants_mut();
|
||||||
|
|
||||||
|
if let Some(mut guard) = global_constants {
|
||||||
|
if let Some(value) = guard.get_mut(var_name) {
|
||||||
|
let mut target: Target = value.clone().into();
|
||||||
|
// Module variables are constant
|
||||||
|
target.set_access_mode(AccessMode::ReadOnly);
|
||||||
|
return Ok((target.into(), *_var_pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(EvalAltResult::ErrorVariableNotFound(
|
||||||
|
format!(
|
||||||
|
"{}{}{}",
|
||||||
|
namespace,
|
||||||
|
Token::DoubleColon.literal_syntax(),
|
||||||
|
var_name
|
||||||
|
),
|
||||||
|
namespace[0].pos,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
Err(
|
Err(
|
||||||
EvalAltResult::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos)
|
EvalAltResult::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos)
|
||||||
.into(),
|
.into(),
|
||||||
@ -1374,7 +1418,7 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(mut new_val) = try_setter {
|
if let Some(mut new_val) = try_setter {
|
||||||
// Try to call index setter if value is changed
|
// Try to call index setter if value is changed
|
||||||
let hash_set = FnCallHashes::from_native(state.fn_hash_idx_set());
|
let hash_set = FnCallHashes::from_native(mods.fn_hash_idx_set());
|
||||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
||||||
|
|
||||||
if let Err(err) = self.exec_fn_call(
|
if let Err(err) = self.exec_fn_call(
|
||||||
@ -1421,7 +1465,7 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(mut new_val) = try_setter {
|
if let Some(mut new_val) = try_setter {
|
||||||
// Try to call index setter
|
// Try to call index setter
|
||||||
let hash_set = FnCallHashes::from_native(state.fn_hash_idx_set());
|
let hash_set = FnCallHashes::from_native(mods.fn_hash_idx_set());
|
||||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -1552,7 +1596,7 @@ impl Engine {
|
|||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
EvalAltResult::ErrorDotExpr(_, _) => {
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
let args = &mut [target, &mut name.into(), &mut new_val];
|
let args = &mut [target, &mut name.into(), &mut new_val];
|
||||||
let hash_set = FnCallHashes::from_native(state.fn_hash_idx_set());
|
let hash_set = FnCallHashes::from_native(mods.fn_hash_idx_set());
|
||||||
let pos = Position::NONE;
|
let pos = Position::NONE;
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -1711,7 +1755,7 @@ impl Engine {
|
|||||||
let args =
|
let args =
|
||||||
&mut [target.as_mut(), &mut name.into(), val];
|
&mut [target.as_mut(), &mut name.into(), val];
|
||||||
let hash_set = FnCallHashes::from_native(
|
let hash_set = FnCallHashes::from_native(
|
||||||
state.fn_hash_idx_set(),
|
mods.fn_hash_idx_set(),
|
||||||
);
|
);
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, hash_set, args,
|
mods, state, lib, FN_IDX_SET, hash_set, args,
|
||||||
@ -1800,7 +1844,7 @@ impl Engine {
|
|||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(_, var_pos, x) => {
|
Expr::Variable(_, var_pos, x) => {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, *var_pos)?;
|
self.inc_operations(&mut mods.num_operations, *var_pos)?;
|
||||||
|
|
||||||
let (mut target, _) =
|
let (mut target, _) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
||||||
@ -1851,7 +1895,7 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, expr.position())?;
|
self.inc_operations(&mut mods.num_operations, expr.position())?;
|
||||||
|
|
||||||
let _parent_chain_type = parent_chain_type;
|
let _parent_chain_type = parent_chain_type;
|
||||||
|
|
||||||
@ -1977,7 +2021,7 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Target<'t>, Box<EvalAltResult>> {
|
) -> Result<Target<'t>, Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, Position::NONE)?;
|
self.inc_operations(&mut mods.num_operations, Position::NONE)?;
|
||||||
|
|
||||||
let mut idx = idx;
|
let mut idx = idx;
|
||||||
let _add_if_not_found = add_if_not_found;
|
let _add_if_not_found = add_if_not_found;
|
||||||
@ -2117,7 +2161,7 @@ impl Engine {
|
|||||||
|
|
||||||
_ if use_indexers => {
|
_ if use_indexers => {
|
||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
let hash_get = FnCallHashes::from_native(state.fn_hash_idx_get());
|
let hash_get = FnCallHashes::from_native(mods.fn_hash_idx_get());
|
||||||
let idx_pos = Position::NONE;
|
let idx_pos = Position::NONE;
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -2150,7 +2194,7 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, expr.position())?;
|
self.inc_operations(&mut mods.num_operations, expr.position())?;
|
||||||
|
|
||||||
let result = match expr {
|
let result = match expr {
|
||||||
Expr::DynamicConstant(x, _) => Ok(x.as_ref().clone()),
|
Expr::DynamicConstant(x, _) => Ok(x.as_ref().clone()),
|
||||||
@ -2502,7 +2546,7 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, stmt.position())?;
|
self.inc_operations(&mut mods.num_operations, stmt.position())?;
|
||||||
|
|
||||||
let result = match stmt {
|
let result = match stmt {
|
||||||
// No-op
|
// No-op
|
||||||
@ -2535,7 +2579,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(&mut mods.num_operations, pos)?;
|
||||||
|
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods,
|
mods,
|
||||||
@ -2688,7 +2732,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, body.position())?;
|
self.inc_operations(&mut mods.num_operations, body.position())?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2812,7 +2856,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, statements.position())?;
|
self.inc_operations(&mut mods.num_operations, statements.position())?;
|
||||||
|
|
||||||
if statements.is_empty() {
|
if statements.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@ -2906,7 +2950,7 @@ impl Engine {
|
|||||||
|
|
||||||
err_map.insert("message".into(), err.to_string().into());
|
err_map.insert("message".into(), err.to_string().into());
|
||||||
|
|
||||||
if let Some(ref source) = state.source {
|
if let Some(ref source) = mods.source {
|
||||||
err_map.insert("source".into(), source.as_str().into());
|
err_map.insert("source".into(), source.as_str().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3002,8 +3046,9 @@ impl Engine {
|
|||||||
|
|
||||||
let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() {
|
let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
|
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
|
||||||
state.global_constants.insert(name.clone(), value.clone());
|
mods.set_global_constant(name, value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
@ -3029,7 +3074,7 @@ impl Engine {
|
|||||||
Stmt::Import(expr, export, _pos) => {
|
Stmt::Import(expr, export, _pos) => {
|
||||||
// Guard against too many modules
|
// Guard against too many modules
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if state.num_modules >= self.max_modules() {
|
if mods.num_modules >= self.max_modules() {
|
||||||
return Err(EvalAltResult::ErrorTooManyModules(*_pos).into());
|
return Err(EvalAltResult::ErrorTooManyModules(*_pos).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3039,10 +3084,10 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
use crate::ModuleResolver;
|
use crate::ModuleResolver;
|
||||||
|
|
||||||
let source = state.source.as_ref().map(|s| s.as_str());
|
let source = mods.source.as_ref().map(|s| s.as_str());
|
||||||
let path_pos = expr.position();
|
let path_pos = expr.position();
|
||||||
|
|
||||||
let module = state
|
let module = mods
|
||||||
.embedded_module_resolver
|
.embedded_module_resolver
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|r| match r.resolve(self, source, &path, path_pos) {
|
.and_then(|r| match r.resolve(self, source, &path, path_pos) {
|
||||||
@ -3076,7 +3121,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.num_modules += 1;
|
mods.num_modules += 1;
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
} else {
|
} else {
|
||||||
@ -3256,19 +3301,19 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
pub(crate) fn inc_operations(
|
pub(crate) fn inc_operations(
|
||||||
&self,
|
&self,
|
||||||
state: &mut EvalState,
|
num_operations: &mut u64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
state.num_operations += 1;
|
*num_operations += 1;
|
||||||
|
|
||||||
// Guard against too many operations
|
// Guard against too many operations
|
||||||
if self.max_operations() > 0 && state.num_operations > self.max_operations() {
|
if self.max_operations() > 0 && *num_operations > self.max_operations() {
|
||||||
return Err(EvalAltResult::ErrorTooManyOperations(pos).into());
|
return Err(EvalAltResult::ErrorTooManyOperations(pos).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report progress - only in steps
|
// Report progress - only in steps
|
||||||
if let Some(ref progress) = self.progress {
|
if let Some(ref progress) = self.progress {
|
||||||
if let Some(token) = progress(state.num_operations) {
|
if let Some(token) = progress(*num_operations) {
|
||||||
// Terminate script if progress returns a termination token
|
// Terminate script if progress returns a termination token
|
||||||
return Err(EvalAltResult::ErrorTerminated(token, pos).into());
|
return Err(EvalAltResult::ErrorTerminated(token, pos).into());
|
||||||
}
|
}
|
||||||
|
@ -1739,10 +1739,12 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut state = EvalState::new();
|
let mut state = EvalState::new();
|
||||||
state.source = ast.source_raw().cloned();
|
if ast.source_raw().is_some() {
|
||||||
|
mods.source = ast.source_raw().cloned();
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
state.embedded_module_resolver = ast.resolver();
|
mods.embedded_module_resolver = ast.resolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
let statements = ast.statements();
|
let statements = ast.statements();
|
||||||
@ -1817,10 +1819,12 @@ impl Engine {
|
|||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
let mods = &mut Imports::new();
|
let mods = &mut Imports::new();
|
||||||
let mut state = EvalState::new();
|
let mut state = EvalState::new();
|
||||||
state.source = ast.source_raw().cloned();
|
if ast.source_raw().is_some() {
|
||||||
|
mods.source = ast.source_raw().cloned();
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
state.embedded_module_resolver = ast.resolver();
|
mods.embedded_module_resolver = ast.resolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
let statements = ast.statements();
|
let statements = ast.statements();
|
||||||
|
@ -179,7 +179,7 @@ impl Engine {
|
|||||||
args: Option<&mut FnCallArgs>,
|
args: Option<&mut FnCallArgs>,
|
||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
is_op_assignment: bool,
|
is_op_assignment: bool,
|
||||||
) -> &'s Option<Box<FnResolutionCacheEntry>> {
|
) -> Option<&'s FnResolutionCacheEntry> {
|
||||||
let mut hash = args.as_ref().map_or(hash_script, |args| {
|
let mut hash = args.as_ref().map_or(hash_script, |args| {
|
||||||
combine_hashes(
|
combine_hashes(
|
||||||
hash_script,
|
hash_script,
|
||||||
@ -187,7 +187,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
&*state
|
let result = state
|
||||||
.fn_resolution_cache_mut()
|
.fn_resolution_cache_mut()
|
||||||
.entry(hash)
|
.entry(hash)
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
@ -295,7 +295,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
result.as_ref().map(Box::as_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a native Rust function registered with the [`Engine`].
|
/// Call a native Rust function registered with the [`Engine`].
|
||||||
@ -307,7 +309,7 @@ impl Engine {
|
|||||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||||
pub(crate) fn call_native_fn(
|
pub(crate) fn call_native_fn(
|
||||||
&self,
|
&self,
|
||||||
mods: &Imports,
|
mods: &mut Imports,
|
||||||
state: &mut EvalState,
|
state: &mut EvalState,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -318,15 +320,14 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(&mut mods.num_operations, pos)?;
|
||||||
|
|
||||||
let state_source = state.source.clone();
|
let parent_source = mods.source.clone();
|
||||||
|
|
||||||
// Check if function access already in the cache
|
// Check if function access already in the cache
|
||||||
let func = self.resolve_fn(mods, state, lib, name, hash, Some(args), true, is_op_assign);
|
let func = self.resolve_fn(mods, state, lib, name, hash, Some(args), true, is_op_assign);
|
||||||
|
|
||||||
if let Some(f) = func {
|
if let Some(FnResolutionCacheEntry { func, source }) = func {
|
||||||
let FnResolutionCacheEntry { func, source } = f.as_ref();
|
|
||||||
assert!(func.is_native());
|
assert!(func.is_native());
|
||||||
|
|
||||||
// Calling pure function but the first argument is a reference?
|
// Calling pure function but the first argument is a reference?
|
||||||
@ -342,18 +343,17 @@ impl Engine {
|
|||||||
// Run external function
|
// Run external function
|
||||||
let source = source
|
let source = source
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.or_else(|| state_source.as_ref())
|
.or_else(|| parent_source.as_ref())
|
||||||
.map(|s| s.as_str());
|
.map(|s| s.as_str());
|
||||||
|
|
||||||
|
let context = (self, name, source, &*mods, lib, pos).into();
|
||||||
|
|
||||||
let result = if func.is_plugin_fn() {
|
let result = if func.is_plugin_fn() {
|
||||||
let context = (self, name, source, mods, lib, pos).into();
|
|
||||||
func.get_plugin_fn()
|
func.get_plugin_fn()
|
||||||
.expect("plugin function")
|
.expect("plugin function")
|
||||||
.call(context, args)
|
.call(context, args)
|
||||||
} else {
|
} else {
|
||||||
let func = func.get_native_fn().expect("native function");
|
func.get_native_fn().expect("native function")(context, args)
|
||||||
let context = (self, name, source, mods, lib, pos).into();
|
|
||||||
func(context, args)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
@ -388,7 +388,7 @@ impl Engine {
|
|||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let source = state.source.as_ref().map(|s| s.as_str());
|
let source = mods.source.as_ref().map(|s| s.as_str());
|
||||||
(debug(&text, source, pos).into(), false)
|
(debug(&text, source, pos).into(), false)
|
||||||
} else {
|
} else {
|
||||||
(Dynamic::UNIT, false)
|
(Dynamic::UNIT, false)
|
||||||
@ -498,7 +498,7 @@ impl Engine {
|
|||||||
fn make_error(
|
fn make_error(
|
||||||
name: String,
|
name: String,
|
||||||
fn_def: &crate::ast::ScriptFnDef,
|
fn_def: &crate::ast::ScriptFnDef,
|
||||||
state: &EvalState,
|
mods: &Imports,
|
||||||
err: Box<EvalAltResult>,
|
err: Box<EvalAltResult>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
@ -508,7 +508,7 @@ impl Engine {
|
|||||||
.lib
|
.lib
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|m| m.id().map(|id| id.to_string()))
|
.and_then(|m| m.id().map(|id| id.to_string()))
|
||||||
.or_else(|| state.source.as_ref().map(|s| s.to_string()))
|
.or_else(|| mods.source.as_ref().map(|s| s.to_string()))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
err,
|
err,
|
||||||
pos,
|
pos,
|
||||||
@ -517,7 +517,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(&mut mods.num_operations, pos)?;
|
||||||
|
|
||||||
if fn_def.body.is_empty() {
|
if fn_def.body.is_empty() {
|
||||||
return Ok(Dynamic::UNIT);
|
return Ok(Dynamic::UNIT);
|
||||||
@ -569,7 +569,6 @@ impl Engine {
|
|||||||
|
|
||||||
// Evaluate the function
|
// Evaluate the function
|
||||||
let body = &fn_def.body;
|
let body = &fn_def.body;
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level)
|
.eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
@ -583,7 +582,7 @@ impl Engine {
|
|||||||
format!("{} @ '{}' < {}", name, src, fn_def.name)
|
format!("{} @ '{}' < {}", name, src, fn_def.name)
|
||||||
};
|
};
|
||||||
|
|
||||||
make_error(fn_name, fn_def, state, err, pos)
|
make_error(fn_name, fn_def, mods, err, pos)
|
||||||
}
|
}
|
||||||
// System errors are passed straight-through
|
// System errors are passed straight-through
|
||||||
mut err if err.is_system_exception() => {
|
mut err if err.is_system_exception() => {
|
||||||
@ -591,7 +590,7 @@ impl Engine {
|
|||||||
Err(err.into())
|
Err(err.into())
|
||||||
}
|
}
|
||||||
// Other errors are wrapped in `ErrorInFunctionCall`
|
// Other errors are wrapped in `ErrorInFunctionCall`
|
||||||
_ => make_error(fn_def.name.to_string(), fn_def, state, err, pos),
|
_ => make_error(fn_def.name.to_string(), fn_def, mods, err, pos),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove all local variables
|
// Remove all local variables
|
||||||
@ -723,11 +722,10 @@ impl Engine {
|
|||||||
let hash_script = hashes.script;
|
let hash_script = hashes.script;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some(f) = hash_script.and_then(|hash| {
|
if let Some(FnResolutionCacheEntry { func, source }) = hash_script.and_then(|hash| {
|
||||||
self.resolve_fn(mods, state, lib, fn_name, hash, None, false, false)
|
self.resolve_fn(mods, state, lib, fn_name, hash, None, false, false)
|
||||||
.clone()
|
.cloned()
|
||||||
}) {
|
}) {
|
||||||
let FnResolutionCacheEntry { func, source } = *f;
|
|
||||||
// Script function call
|
// Script function call
|
||||||
assert!(func.is_script());
|
assert!(func.is_script());
|
||||||
|
|
||||||
@ -759,8 +757,8 @@ impl Engine {
|
|||||||
.split_first_mut()
|
.split_first_mut()
|
||||||
.expect("method call has first parameter");
|
.expect("method call has first parameter");
|
||||||
|
|
||||||
let orig_source = state.source.take();
|
let orig_source = mods.source.take();
|
||||||
state.source = source;
|
mods.source = source;
|
||||||
|
|
||||||
let level = _level + 1;
|
let level = _level + 1;
|
||||||
|
|
||||||
@ -777,7 +775,7 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Restore the original source
|
// Restore the original source
|
||||||
state.source = orig_source;
|
mods.source = orig_source;
|
||||||
|
|
||||||
result?
|
result?
|
||||||
} else {
|
} else {
|
||||||
@ -792,8 +790,8 @@ impl Engine {
|
|||||||
.change_first_arg_to_copy(args);
|
.change_first_arg_to_copy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_source = state.source.take();
|
let orig_source = mods.source.take();
|
||||||
state.source = source;
|
mods.source = source;
|
||||||
|
|
||||||
let level = _level + 1;
|
let level = _level + 1;
|
||||||
|
|
||||||
@ -801,7 +799,7 @@ impl Engine {
|
|||||||
self.call_script_fn(scope, mods, state, lib, &mut None, func, args, pos, level);
|
self.call_script_fn(scope, mods, state, lib, &mut None, func, args, pos, level);
|
||||||
|
|
||||||
// Restore the original source
|
// Restore the original source
|
||||||
state.source = orig_source;
|
mods.source = orig_source;
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
if let Some(bk) = backup {
|
if let Some(bk) = backup {
|
||||||
@ -848,14 +846,13 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
state: &mut EvalState,
|
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
script: &str,
|
script: &str,
|
||||||
_pos: Position,
|
_pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, _pos)?;
|
self.inc_operations(&mut mods.num_operations, _pos)?;
|
||||||
|
|
||||||
let script = script.trim();
|
let script = script.trim();
|
||||||
if script.is_empty() {
|
if script.is_empty() {
|
||||||
@ -882,16 +879,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the AST
|
// Evaluate the AST
|
||||||
let mut new_state = EvalState::new();
|
self.eval_global_statements(scope, mods, &mut EvalState::new(), statements, lib, level)
|
||||||
new_state.source = state.source.clone();
|
|
||||||
new_state.num_operations = state.num_operations;
|
|
||||||
|
|
||||||
let result =
|
|
||||||
self.eval_global_statements(scope, mods, &mut new_state, statements, lib, level);
|
|
||||||
|
|
||||||
state.num_operations = new_state.num_operations;
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a dot method.
|
/// Call a dot method.
|
||||||
@ -1230,7 +1218,7 @@ impl Engine {
|
|||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
||||||
let result =
|
let result =
|
||||||
self.eval_script_expr_in_place(scope, mods, state, lib, script, pos, level + 1);
|
self.eval_script_expr_in_place(scope, mods, lib, script, pos, level + 1);
|
||||||
|
|
||||||
// IMPORTANT! If the eval defines new variables in the current scope,
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||||
// all variable offsets from this point on will be mis-aligned.
|
// all variable offsets from this point on will be mis-aligned.
|
||||||
@ -1241,8 +1229,7 @@ impl Engine {
|
|||||||
return result.map_err(|err| {
|
return result.map_err(|err| {
|
||||||
EvalAltResult::ErrorInFunctionCall(
|
EvalAltResult::ErrorInFunctionCall(
|
||||||
KEYWORD_EVAL.to_string(),
|
KEYWORD_EVAL.to_string(),
|
||||||
state
|
mods.source
|
||||||
.source
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(Identifier::to_string)
|
.map(Identifier::to_string)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
@ -1289,7 +1276,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, _pos)?;
|
self.inc_operations(&mut mods.num_operations, _pos)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
@ -1369,7 +1356,7 @@ impl Engine {
|
|||||||
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, _pos)?;
|
self.inc_operations(&mut mods.num_operations, _pos)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
@ -1410,7 +1397,7 @@ impl Engine {
|
|||||||
// Then search in Rust functions
|
// Then search in Rust functions
|
||||||
None => {
|
None => {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(&mut mods.num_operations, pos)?;
|
||||||
|
|
||||||
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
||||||
let hash_qualified_fn = combine_hashes(hash, hash_params);
|
let hash_qualified_fn = combine_hashes(hash, hash_params);
|
||||||
@ -1439,7 +1426,7 @@ impl Engine {
|
|||||||
let new_scope = &mut Scope::new();
|
let new_scope = &mut Scope::new();
|
||||||
|
|
||||||
let mut source = module.id_raw().cloned();
|
let mut source = module.id_raw().cloned();
|
||||||
mem::swap(&mut state.source, &mut source);
|
mem::swap(&mut mods.source, &mut source);
|
||||||
|
|
||||||
let level = level + 1;
|
let level = level + 1;
|
||||||
|
|
||||||
@ -1447,7 +1434,7 @@ impl Engine {
|
|||||||
new_scope, mods, state, lib, &mut None, fn_def, &mut args, pos, level,
|
new_scope, mods, state, lib, &mut None, fn_def, &mut args, pos, level,
|
||||||
);
|
);
|
||||||
|
|
||||||
state.source = source;
|
mods.source = source;
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ impl<'a> OptimizerState<'a> {
|
|||||||
) -> Option<Dynamic> {
|
) -> Option<Dynamic> {
|
||||||
self.engine
|
self.engine
|
||||||
.call_native_fn(
|
.call_native_fn(
|
||||||
&Imports::new(),
|
&mut Imports::new(),
|
||||||
&mut EvalState::new(),
|
&mut EvalState::new(),
|
||||||
self.lib,
|
self.lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
|
@ -96,21 +96,6 @@ fn test_functions_global_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, _) if v == "global::ANSWER")
|
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, _) if v == "global::ANSWER")
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut module = Module::new();
|
|
||||||
module.set_var("ANSWER", 123 as INT);
|
|
||||||
engine.register_static_module("global", module.into());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
"
|
|
||||||
const ANSWER = 42;
|
|
||||||
fn foo() { global::ANSWER }
|
|
||||||
foo()
|
|
||||||
"
|
|
||||||
)?,
|
|
||||||
123
|
|
||||||
);
|
|
||||||
|
|
||||||
engine.register_result_fn(
|
engine.register_result_fn(
|
||||||
"do_stuff",
|
"do_stuff",
|
||||||
|context: NativeCallContext, callback: rhai::FnPtr| {
|
|context: NativeCallContext, callback: rhai::FnPtr| {
|
||||||
@ -129,5 +114,32 @@ fn test_functions_global_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, _) if v == "global::LOCAL_VALUE")
|
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, _) if v == "global::LOCAL_VALUE")
|
||||||
));
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
const GLOBAL_VALUE = 42;
|
||||||
|
do_stuff(|| global::GLOBAL_VALUE);
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
// Override global
|
||||||
|
let mut module = Module::new();
|
||||||
|
module.set_var("ANSWER", 123 as INT);
|
||||||
|
engine.register_static_module("global", module.into());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
const ANSWER = 42;
|
||||||
|
fn foo() { global::ANSWER }
|
||||||
|
foo()
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user