Reduce Module size.

This commit is contained in:
Stephen Chung 2022-11-24 22:25:19 +08:00
parent bda8c2b636
commit cefe3f1715
6 changed files with 322 additions and 247 deletions

View File

@ -3,8 +3,8 @@
use crate::func::RegisterNativeFunction;
use crate::types::dynamic::Variant;
use crate::{
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext,
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST,
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, NativeCallContext,
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, TypeBuilder, AST,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -534,7 +534,7 @@ impl Position {
}
#[allow(deprecated)]
impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> {
impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
/// Register a custom fallible function.
///
/// # Deprecated
@ -642,3 +642,19 @@ impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> {
self.with_indexer_set(set_fn)
}
}
impl Module {
/// Create a new [`Module`] with a pre-sized capacity for functions.
///
/// # Deprecated
///
/// This method is deprecated. Use `new` instead.
///
/// This method will be removed in the next major version.
#[inline(always)]
#[must_use]
#[deprecated(since = "1.12.0", note = "use `new` instead")]
pub fn with_capacity(_capacity: usize) -> Self {
Self::new()
}
}

View File

@ -303,7 +303,7 @@ impl Engine {
};
// Add the global namespace module
let mut global_namespace = Module::with_capacity(0);
let mut global_namespace = Module::new();
global_namespace.flags |= ModuleFlags::INTERNAL;
engine.global_modules.push(global_namespace.into());

View File

@ -173,12 +173,13 @@ impl GlobalRuntimeState {
name: impl Into<ImmutableString>,
module: impl Into<crate::SharedModule>,
) {
if self.imports.is_none() {
self.imports = Some(crate::StaticVec::new_const().into());
self.modules = Some(crate::StaticVec::new_const().into());
}
self.imports.as_mut().unwrap().push(name.into());
self.modules.as_mut().unwrap().push(module.into());
self.imports
.get_or_insert_with(|| crate::StaticVec::new_const().into())
.push(name.into());
self.modules
.get_or_insert_with(|| crate::StaticVec::new_const().into())
.push(module.into());
}
/// Truncate the stack of globally-imported [modules][crate::Module] to a particular length.
///
@ -352,13 +353,12 @@ impl GlobalRuntimeState {
impl<K: Into<ImmutableString>, M: Into<crate::SharedModule>> Extend<(K, M)> for GlobalRuntimeState {
#[inline]
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
if self.imports.is_none() {
self.imports = Some(crate::StaticVec::new_const().into());
self.modules = Some(crate::StaticVec::new_const().into());
}
let imports = self.imports.as_mut().unwrap();
let modules = self.modules.as_mut().unwrap();
let imports = self
.imports
.get_or_insert_with(|| crate::StaticVec::new_const().into());
let modules = self
.modules
.get_or_insert_with(|| crate::StaticVec::new_const().into());
for (k, m) in iter {
imports.push(k.into());

View File

@ -177,26 +177,26 @@ pub struct Module {
id: Option<ImmutableString>,
/// Module documentation.
#[cfg(feature = "metadata")]
doc: crate::SmartString,
doc: Option<crate::SmartString>,
/// Custom types.
custom_types: Option<CustomTypesCollection>,
custom_types: Option<Box<CustomTypesCollection>>,
/// Sub-modules.
modules: Option<BTreeMap<Identifier, SharedModule>>,
modules: Option<Box<BTreeMap<Identifier, SharedModule>>>,
/// [`Module`] variables.
variables: Option<BTreeMap<Identifier, Dynamic>>,
variables: Option<Box<BTreeMap<Identifier, Dynamic>>>,
/// Flattened collection of all [`Module`] variables, including those in sub-modules.
all_variables: Option<StraightHashMap<Dynamic>>,
all_variables: Option<Box<StraightHashMap<Dynamic>>>,
/// Functions (both native Rust and scripted).
functions: StraightHashMap<FuncInfo>,
functions: Option<Box<StraightHashMap<FuncInfo>>>,
/// Flattened collection of all functions, native Rust and scripted.
/// including those in sub-modules.
all_functions: Option<StraightHashMap<CallableFunction>>,
all_functions: Option<Box<StraightHashMap<CallableFunction>>>,
/// Bloom filter on native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters.
dynamic_functions_filter: BloomFilterU64,
/// Iterator functions, keyed by the type producing the iterator.
type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>,
type_iterators: Option<Box<BTreeMap<TypeId, Shared<IteratorFn>>>>,
/// Flattened collection of iterator functions, including those in sub-modules.
all_type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>,
all_type_iterators: Option<Box<BTreeMap<TypeId, Shared<IteratorFn>>>>,
/// Flags.
pub(crate) flags: ModuleFlags,
}
@ -282,31 +282,16 @@ impl Module {
/// ```
#[inline(always)]
#[must_use]
pub fn new() -> Self {
Self::with_capacity(16)
}
/// Create a new [`Module`] with a pre-sized capacity for functions.
///
/// # Example
///
/// ```
/// # use rhai::Module;
/// let mut module = Module::with_capacity(10);
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
/// ```
#[inline]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
pub const fn new() -> Self {
Self {
id: None,
#[cfg(feature = "metadata")]
doc: crate::SmartString::new_const(),
doc: None,
custom_types: None,
modules: None,
variables: None,
all_variables: None,
functions: StraightHashMap::with_capacity_and_hasher(capacity, Default::default()),
functions: None,
all_functions: None,
dynamic_functions_filter: BloomFilterU64::new(),
type_iterators: None,
@ -396,7 +381,7 @@ impl Module {
#[inline]
#[must_use]
pub fn doc(&self) -> &str {
&self.doc
self.doc.as_ref().map_or("", |s| s.as_str())
}
/// Set the documentation of the [`Module`].
@ -415,7 +400,7 @@ impl Module {
#[cfg(feature = "metadata")]
#[inline(always)]
pub fn set_doc(&mut self, doc: impl Into<crate::SmartString>) -> &mut Self {
self.doc = doc.into();
self.doc = Some(doc.into());
self
}
@ -434,7 +419,7 @@ impl Module {
#[cfg(feature = "metadata")]
#[inline(always)]
pub fn clear_doc(&mut self) -> &mut Self {
self.doc.clear();
self.doc = None;
self
}
@ -442,12 +427,14 @@ impl Module {
#[inline(always)]
pub fn clear(&mut self) {
#[cfg(feature = "metadata")]
self.doc.clear();
{
self.doc = None;
}
self.custom_types = None;
self.modules = None;
self.variables = None;
self.all_variables = None;
self.functions.clear();
self.functions = None;
self.all_functions = None;
self.dynamic_functions_filter.clear();
self.type_iterators = None;
@ -475,7 +462,7 @@ impl Module {
#[inline(always)]
pub fn set_custom_type<T>(&mut self, name: &str) -> &mut Self {
self.custom_types
.get_or_insert_with(CustomTypesCollection::new)
.get_or_insert_with(|| CustomTypesCollection::new().into())
.add_type::<T>(name);
self
}
@ -501,7 +488,7 @@ impl Module {
name: impl Into<Identifier>,
) -> &mut Self {
self.custom_types
.get_or_insert_with(CustomTypesCollection::new)
.get_or_insert_with(|| CustomTypesCollection::new().into())
.add(type_path, name);
self
}
@ -544,7 +531,7 @@ impl Module {
#[must_use]
pub fn is_empty(&self) -> bool {
!self.flags.contains(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
&& self.functions.is_empty()
&& self.functions.as_ref().map_or(true, |m| m.is_empty())
&& self.variables.as_ref().map_or(true, |m| m.is_empty())
&& self.modules.as_ref().map_or(true, |m| m.is_empty())
&& self.type_iterators.as_ref().map_or(true, |t| t.is_empty())
@ -700,23 +687,25 @@ impl Module {
let hash_script = crate::calc_fn_hash(None, &fn_def.name, num_params);
#[cfg(feature = "metadata")]
let params_info = fn_def.params.iter().map(Into::into).collect();
self.functions.insert(
hash_script,
FuncInfo {
name: fn_def.name.as_str().into(),
namespace: FnNamespace::Internal,
access: fn_def.access,
num_params,
param_types: StaticVec::new_const(),
#[cfg(feature = "metadata")]
params_info,
#[cfg(feature = "metadata")]
return_type: "".into(),
#[cfg(feature = "metadata")]
comments: Box::default(),
func: fn_def.into(),
},
);
self.functions
.get_or_insert_with(|| StraightHashMap::default().into())
.insert(
hash_script,
FuncInfo {
name: fn_def.name.as_str().into(),
namespace: FnNamespace::Internal,
access: fn_def.access,
num_params,
param_types: StaticVec::new_const(),
#[cfg(feature = "metadata")]
params_info,
#[cfg(feature = "metadata")]
return_type: "".into(),
#[cfg(feature = "metadata")]
comments: Box::default(),
func: fn_def.into(),
},
);
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
hash_script
}
@ -731,15 +720,13 @@ impl Module {
name: impl AsRef<str>,
num_params: usize,
) -> Option<&Shared<crate::ast::ScriptFnDef>> {
if self.functions.is_empty() {
None
} else {
self.functions.as_ref().and_then(|lib| {
let name = name.as_ref();
self.iter_fn()
.find(|f| f.num_params == num_params && f.name == name)
lib.values()
.find(|&f| f.num_params == num_params && f.name == name)
.and_then(|f| f.func.get_script_fn_def())
}
})
}
/// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules,
@ -840,14 +827,12 @@ impl Module {
/// let hash = module.set_native_fn("calc", || Ok(42_i64));
/// assert!(module.contains_fn(hash));
/// ```
#[inline(always)]
#[inline]
#[must_use]
pub fn contains_fn(&self, hash_fn: u64) -> bool {
if self.functions.is_empty() {
false
} else {
self.functions.contains_key(&hash_fn)
}
self.functions
.as_ref()
.map_or(false, |m| m.contains_key(&hash_fn))
}
/// _(metadata)_ Update the metadata (parameter names/types and return type) of a registered function.
@ -876,7 +861,7 @@ impl Module {
.map(|s| s.as_ref().into())
.collect();
if let Some(f) = self.functions.get_mut(&hash_fn) {
if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
let (param_names, return_type_name) = if param_names.len() > f.num_params {
let return_type = param_names.pop().unwrap();
(param_names, return_type)
@ -925,7 +910,11 @@ impl Module {
let comments = comments.as_ref();
if !comments.is_empty() {
let f = self.functions.get_mut(&hash_fn).unwrap();
let f = self
.functions
.as_mut()
.and_then(|m| m.get_mut(&hash_fn))
.unwrap();
f.comments = comments.iter().map(|s| s.as_ref().into()).collect();
}
@ -937,7 +926,7 @@ impl Module {
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
#[inline]
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
if let Some(f) = self.functions.get_mut(&hash_fn) {
if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
f.namespace = namespace;
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
}
@ -1029,23 +1018,27 @@ impl Module {
self.dynamic_functions_filter.mark(hash_script);
}
self.functions.insert(
hash_fn,
FuncInfo {
func,
name: name.into(),
namespace,
access,
num_params: param_types.len(),
param_types,
#[cfg(feature = "metadata")]
params_info: param_names,
#[cfg(feature = "metadata")]
return_type: return_type_name,
#[cfg(feature = "metadata")]
comments: Box::default(),
},
);
self.functions
.get_or_insert_with(|| {
StraightHashMap::with_capacity_and_hasher(16, Default::default()).into()
})
.insert(
hash_fn,
FuncInfo {
func,
name: name.into(),
namespace,
access,
num_params: param_types.len(),
param_types,
#[cfg(feature = "metadata")]
params_info: param_names,
#[cfg(feature = "metadata")]
return_type: return_type_name,
#[cfg(feature = "metadata")]
comments: Box::default(),
},
);
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
@ -1095,7 +1088,7 @@ impl Module {
let comments = comments.as_ref();
if !comments.is_empty() {
let f = self.functions.get_mut(&hash).unwrap();
let f = self.functions.as_mut().unwrap().get_mut(&hash).unwrap();
f.comments = comments.iter().map(|s| s.as_ref().into()).collect();
}
@ -1533,11 +1526,10 @@ impl Module {
#[inline]
#[must_use]
pub(crate) fn get_fn(&self, hash_native: u64) -> Option<&CallableFunction> {
if self.functions.is_empty() {
None
} else {
self.functions.get(&hash_native).map(|f| &f.func)
}
self.functions
.as_ref()
.and_then(|m| m.get(&hash_native))
.map(|f| &f.func)
}
/// Can the particular function with [`Dynamic`] parameter(s) exist in the [`Module`]?
@ -1590,7 +1582,13 @@ impl Module {
Some(_) => (),
None => self.variables = other.variables,
}
self.functions.extend(other.functions.into_iter());
match self.functions {
Some(ref mut m) if other.functions.is_some() => {
m.extend(other.functions.unwrap().into_iter())
}
Some(_) => (),
None => self.functions = other.functions,
}
self.dynamic_functions_filter += &other.dynamic_functions_filter;
match self.type_iterators {
Some(ref mut m) if other.type_iterators.is_some() => {
@ -1605,11 +1603,15 @@ impl Module {
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
#[cfg(feature = "metadata")]
if !other.doc.is_empty() {
if !self.doc.is_empty() {
self.doc.push('\n');
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
self.doc
.get_or_insert_with(|| SmartString::new_const())
.push('\n');
}
self.doc.push_str(&other.doc);
self.doc
.get_or_insert_with(|| SmartString::new_const())
.push_str(other.doc.as_ref().unwrap());
}
self
@ -1632,7 +1634,13 @@ impl Module {
Some(_) => (),
None => self.variables = other.variables,
}
self.functions.extend(other.functions.into_iter());
match self.functions {
Some(ref mut m) if other.functions.is_some() => {
m.extend(other.functions.unwrap().into_iter())
}
Some(_) => (),
None => self.functions = other.functions,
}
self.dynamic_functions_filter += &other.dynamic_functions_filter;
match self.type_iterators {
Some(ref mut m) if other.type_iterators.is_some() => {
@ -1647,11 +1655,15 @@ impl Module {
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
#[cfg(feature = "metadata")]
if !other.doc.is_empty() {
if !self.doc.is_empty() {
self.doc.push('\n');
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
self.doc
.get_or_insert_with(|| SmartString::new_const())
.push('\n');
}
self.doc.push_str(&other.doc);
self.doc
.get_or_insert_with(|| SmartString::new_const())
.push_str(other.doc.as_ref().unwrap());
}
self
@ -1664,23 +1676,29 @@ impl Module {
if let Some(ref modules) = other.modules {
let m = self.modules.get_or_insert_with(|| Default::default());
for (k, v) in modules {
for (k, v) in modules.iter() {
if !m.contains_key(k) {
m.insert(k.clone(), v.clone());
}
}
}
if let Some(ref variables) = other.variables {
for (k, v) in variables {
let m = self.variables.get_or_insert_with(|| Default::default());
for (k, v) in variables.iter() {
let map = self.variables.get_or_insert_with(|| Default::default());
if !m.contains_key(k) {
m.insert(k.clone(), v.clone());
if !map.contains_key(k) {
map.insert(k.clone(), v.clone());
}
}
}
for (&k, v) in &other.functions {
self.functions.entry(k).or_insert_with(|| v.clone());
if let Some(ref functions) = other.functions {
for (k, f) in functions.iter() {
let map = self.functions.get_or_insert_with(|| Default::default());
if !map.contains_key(k) {
map.insert(*k, f.clone());
}
}
}
self.dynamic_functions_filter += &other.dynamic_functions_filter;
if let Some(ref type_iterators) = other.type_iterators {
@ -1688,7 +1706,7 @@ impl Module {
.type_iterators
.get_or_insert_with(|| Default::default());
for (&k, v) in type_iterators {
for (&k, v) in type_iterators.iter() {
t.entry(k).or_insert_with(|| v.clone());
}
}
@ -1698,11 +1716,15 @@ impl Module {
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
#[cfg(feature = "metadata")]
if !other.doc.is_empty() {
if !self.doc.is_empty() {
self.doc.push('\n');
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
self.doc
.get_or_insert_with(|| SmartString::new_const())
.push('\n');
}
self.doc.push_str(&other.doc);
self.doc
.get_or_insert_with(|| SmartString::new_const())
.push_str(other.doc.as_ref().unwrap());
}
self
@ -1721,7 +1743,7 @@ impl Module {
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool + Copy,
) -> &mut Self {
if let Some(ref modules) = other.modules {
for (k, v) in modules {
for (k, v) in modules.iter() {
let mut m = Self::new();
m.merge_filtered(v, _filter);
self.set_sub_module(k.clone(), m);
@ -1740,22 +1762,25 @@ impl Module {
None => self.variables = other.variables.clone(),
}
}
self.functions.extend(
other
.functions
.iter()
.filter(|&(.., f)| {
_filter(
f.namespace,
f.access,
f.func.is_script(),
f.name.as_str(),
f.num_params,
)
})
.map(|(&k, v)| (k, v.clone())),
);
if let Some(ref functions) = other.functions {
match self.functions {
Some(ref mut m) => m.extend(
functions
.iter()
.filter(|&(.., f)| {
_filter(
f.namespace,
f.access,
f.func.is_script(),
f.name.as_str(),
f.num_params,
)
})
.map(|(&k, f)| (k, f.clone())),
),
None => self.functions = other.functions.clone(),
}
}
self.dynamic_functions_filter += &other.dynamic_functions_filter;
if let Some(ref type_iterators) = other.type_iterators {
@ -1770,11 +1795,15 @@ impl Module {
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
#[cfg(feature = "metadata")]
if !other.doc.is_empty() {
if !self.doc.is_empty() {
self.doc.push('\n');
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
self.doc
.get_or_insert_with(|| SmartString::new_const())
.push('\n');
}
self.doc.push_str(&other.doc);
self.doc
.get_or_insert_with(|| SmartString::new_const())
.push_str(other.doc.as_ref().unwrap());
}
self
@ -1787,16 +1816,19 @@ impl Module {
&mut self,
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
) -> &mut Self {
self.functions = std::mem::take(&mut self.functions)
.into_iter()
.filter(|(.., f)| {
if f.func.is_script() {
filter(f.namespace, f.access, f.name.as_str(), f.num_params)
} else {
false
}
})
.collect();
self.functions = std::mem::take(&mut self.functions).map(|m| {
Box::new(
m.into_iter()
.filter(|(.., f)| {
if f.func.is_script() {
filter(f.namespace, f.access, f.name.as_str(), f.num_params)
} else {
false
}
})
.collect(),
)
});
self.dynamic_functions_filter.clear();
self.all_functions = None;
@ -1812,7 +1844,7 @@ impl Module {
pub fn count(&self) -> (usize, usize, usize) {
(
self.variables.as_ref().map_or(0, |m| m.len()),
self.functions.len(),
self.functions.as_ref().map_or(0, |m| m.len()),
self.type_iterators.as_ref().map_or(0, |t| t.len()),
)
}
@ -1837,7 +1869,7 @@ impl Module {
#[inline]
#[allow(dead_code)]
pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
self.functions.values()
self.functions.iter().flat_map(|m| m.values())
}
/// Get an iterator over all script-defined functions in the [`Module`].
@ -2117,7 +2149,7 @@ impl Module {
let mut contains_indexed_global_functions = false;
if let Some(ref modules) = module.modules {
for (name, m) in modules {
for (name, m) in modules.iter() {
// Index all the sub-modules first.
path.push(name);
if index_module(m, path, variables, functions, type_iterators) {
@ -2129,7 +2161,7 @@ impl Module {
// Index all variables
if let Some(ref v) = module.variables {
for (var_name, value) in v {
for (var_name, value) in v.iter() {
let hash_var = crate::calc_var_hash(path.iter().copied(), var_name);
variables.insert(hash_var, value.clone());
}
@ -2137,13 +2169,13 @@ impl Module {
// Index type iterators
if let Some(ref t) = module.type_iterators {
for (&type_id, func) in t {
for (&type_id, func) in t.iter() {
type_iterators.insert(type_id, func.clone());
}
}
// Index all Rust functions
for (&hash, f) in &module.functions {
for (&hash, f) in module.functions.iter().flat_map(|m| m.iter()) {
match f.namespace {
FnNamespace::Global => {
// Flatten all functions with global namespace
@ -2194,17 +2226,17 @@ impl Module {
self.all_variables = if variables.is_empty() {
None
} else {
Some(variables)
Some(variables.into())
};
self.all_functions = if functions.is_empty() {
None
} else {
Some(functions)
Some(functions.into())
};
self.all_type_iterators = if type_iterators.is_empty() {
None
} else {
Some(type_iterators)
Some(type_iterators.into())
};
self.flags |= ModuleFlags::INDEXED;

View File

@ -200,13 +200,12 @@ impl<'e, 's> ParseState<'e, 's> {
.flat_map(|v| v.iter())
.any(|v| v.as_str() == name)
{
if self.external_vars.is_none() {
self.external_vars = Some(FnArgsVec::new().into());
}
self.external_vars.as_mut().unwrap().push(Ident {
name: name.into(),
pos: _pos,
});
self.external_vars
.get_or_insert_with(|| FnArgsVec::new().into())
.push(Ident {
name: name.into(),
pos: _pos,
});
}
} else {
self.allow_capture = true;
@ -2040,7 +2039,7 @@ impl Engine {
}
// var (indexed) = rhs
Expr::Variable(ref x, i, var_pos) => {
let stack = state.stack.as_mut().unwrap();
let stack = state.stack.get_or_insert_with(|| Scope::new().into());
let (index, .., name) = &**x;
let index = i.map_or_else(
|| index.expect("either long or short index is `None`").get(),
@ -2462,10 +2461,10 @@ impl Engine {
// Add a barrier variable to the stack so earlier variables will not be matched.
// Variable searches stop at the first barrier.
let marker = state.get_interned_string(SCOPE_SEARCH_BARRIER_MARKER);
if state.stack.is_none() {
state.stack = Some(Scope::new().into());
}
state.stack.as_mut().unwrap().push(marker, ());
state
.stack
.get_or_insert_with(|| Scope::new().into())
.push(marker, ());
}
let mut user_state = Dynamic::UNIT;
@ -2817,26 +2816,29 @@ impl Engine {
.parse_expr(input, state, lib, settings.level_up()?)?
.ensure_iterable()?;
if state.stack.is_none() {
state.stack = Some(Scope::new().into());
}
let prev_stack_len = state.stack.as_mut().unwrap().len();
if !counter_name.is_empty() {
state.stack.as_mut().unwrap().push(name.clone(), ());
}
let counter_var = Ident {
name: state.get_interned_string(counter_name),
pos: counter_pos,
};
let loop_var = state.get_interned_string(name);
state.stack.as_mut().unwrap().push(loop_var.clone(), ());
let loop_var = Ident {
name: loop_var,
name: state.get_interned_string(name),
pos: name_pos,
};
let prev_stack_len = {
let stack = state.stack.get_or_insert_with(|| Scope::new().into());
let prev_stack_len = stack.len();
if !counter_var.name.is_empty() {
stack.push(counter_var.name.clone(), ());
}
stack.push(&loop_var.name, ());
prev_stack_len
};
settings.flags |= ParseSettingFlags::BREAKABLE;
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
@ -2865,49 +2867,41 @@ impl Engine {
// let name ...
let (name, pos) = parse_var_name(input)?;
if state.stack.is_none() {
state.stack = Some(Scope::new().into());
}
if !self.allow_shadowing()
&& state
.stack
.as_ref()
.unwrap()
.iter()
.any(|(v, ..)| v == name)
{
return Err(PERR::VariableExists(name.into()).into_err(pos));
}
let stack = state.stack.get_or_insert_with(|| Scope::new().into());
if let Some(ref filter) = self.def_var_filter {
let stack = state.stack.as_mut().unwrap();
let will_shadow = stack.iter().any(|(v, ..)| v == name);
if state.global.is_none() {
state.global = Some(GlobalRuntimeState::new(self).into());
if !self.allow_shadowing() && stack.iter().any(|(v, ..)| v == name) {
return Err(PERR::VariableExists(name.into()).into_err(pos));
}
let global = state.global.as_mut().unwrap();
global.level = settings.level;
let is_const = access == AccessMode::ReadOnly;
let info = VarDefInfo {
name: &name,
is_const,
nesting_level: settings.level,
will_shadow,
};
let caches = &mut Caches::new();
let mut this = Dynamic::NULL;
if let Some(ref filter) = self.def_var_filter {
let will_shadow = stack.iter().any(|(v, ..)| v == name);
let context = EvalContext::new(self, global, caches, stack, &mut this);
let global = state
.global
.get_or_insert_with(|| GlobalRuntimeState::new(self).into());
match filter(false, info, context) {
Ok(true) => (),
Ok(false) => return Err(PERR::ForbiddenVariable(name.into()).into_err(pos)),
Err(err) => match *err {
EvalAltResult::ErrorParsing(e, pos) => return Err(e.into_err(pos)),
_ => return Err(PERR::ForbiddenVariable(name.into()).into_err(pos)),
},
global.level = settings.level;
let is_const = access == AccessMode::ReadOnly;
let info = VarDefInfo {
name: &name,
is_const,
nesting_level: settings.level,
will_shadow,
};
let caches = &mut Caches::new();
let mut this = Dynamic::NULL;
let context = EvalContext::new(self, global, caches, stack, &mut this);
match filter(false, info, context) {
Ok(true) => (),
Ok(false) => return Err(PERR::ForbiddenVariable(name.into()).into_err(pos)),
Err(err) => match *err {
EvalAltResult::ErrorParsing(e, pos) => return Err(e.into_err(pos)),
_ => return Err(PERR::ForbiddenVariable(name.into()).into_err(pos)),
},
}
}
}
@ -2945,7 +2939,7 @@ impl Engine {
let idx = if let Some(n) = existing {
stack.get_mut_by_index(n).set_access_mode(access);
Some(NonZeroUsize::new(state.stack.as_mut().unwrap().len() - n).unwrap())
Some(NonZeroUsize::new(stack.len() - n).unwrap())
} else {
stack.push_entry(name.as_str(), access, Dynamic::UNIT);
None
@ -3470,12 +3464,11 @@ impl Engine {
.into_err(err_pos));
}
if state.stack.is_none() {
state.stack = Some(Scope::new().into());
}
let name = state.get_interned_string(name);
state.stack.as_mut().unwrap().push(name.clone(), ());
state
.stack
.get_or_insert_with(|| Scope::new().into())
.push(name.clone(), ());
Ident { name, pos }
} else {
Ident {
@ -3489,8 +3482,7 @@ impl Engine {
if !catch_var.is_empty() {
// Remove the error variable from the stack
let stack = state.stack.as_mut().unwrap();
stack.rewind(stack.len() - 1);
state.stack.as_mut().unwrap().pop();
}
Ok(Stmt::TryCatch(
@ -3554,12 +3546,11 @@ impl Engine {
);
}
if state.stack.is_none() {
state.stack = Some(Scope::new().into());
}
let s = state.get_interned_string(*s);
state.stack.as_mut().unwrap().push(s.clone(), ());
state
.stack
.get_or_insert_with(|| Scope::new().into())
.push(s.clone(), ());
params.push((s, pos));
}
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
@ -3700,12 +3691,11 @@ impl Engine {
);
}
if state.stack.is_none() {
state.stack = Some(Scope::new().into());
}
let s = state.get_interned_string(*s);
state.stack.as_mut().unwrap().push(s.clone(), ());
state
.stack
.get_or_insert_with(|| Scope::new().into())
.push(s.clone(), ());
params_list.push(s);
}
(Token::LexError(err), pos) => return Err(err.into_err(pos)),

View File

@ -353,6 +353,43 @@ impl Scope<'_> {
self.values.push(value);
self
}
/// Remove the last entry from the [`Scope`].
///
/// # Panics
///
/// Panics is the [`Scope`] is empty.
///
/// # Example
///
/// ```
/// use rhai::Scope;
///
/// let mut my_scope = Scope::new();
///
/// my_scope.push("x", 42_i64);
/// my_scope.push("y", 123_i64);
/// assert!(my_scope.contains("x"));
/// assert!(my_scope.contains("y"));
/// assert_eq!(my_scope.len(), 2);
///
/// my_scope.pop();
/// assert!(my_scope.contains("x"));
/// assert!(!my_scope.contains("y"));
/// assert_eq!(my_scope.len(), 1);
///
/// my_scope.pop();
/// assert!(!my_scope.contains("x"));
/// assert!(!my_scope.contains("y"));
/// assert_eq!(my_scope.len(), 0);
/// assert!(my_scope.is_empty());
/// ```
#[inline(always)]
pub fn pop(&mut self) -> &mut Self {
self.names.pop().expect("`Scope` must not be empty");
self.values.pop().expect("`Scope` must not be empty");
self.aliases.pop().expect("`Scope` must not be empty");
self
}
/// Truncate (rewind) the [`Scope`] to a previous size.
///
/// # Example