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::func::RegisterNativeFunction;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext, Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, NativeCallContext,
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, TypeBuilder, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -534,7 +534,7 @@ impl Position {
} }
#[allow(deprecated)] #[allow(deprecated)]
impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> { impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
/// Register a custom fallible function. /// Register a custom fallible function.
/// ///
/// # Deprecated /// # Deprecated
@ -642,3 +642,19 @@ impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> {
self.with_indexer_set(set_fn) 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 // Add the global namespace module
let mut global_namespace = Module::with_capacity(0); let mut global_namespace = Module::new();
global_namespace.flags |= ModuleFlags::INTERNAL; global_namespace.flags |= ModuleFlags::INTERNAL;
engine.global_modules.push(global_namespace.into()); engine.global_modules.push(global_namespace.into());

View File

@ -173,12 +173,13 @@ impl GlobalRuntimeState {
name: impl Into<ImmutableString>, name: impl Into<ImmutableString>,
module: impl Into<crate::SharedModule>, module: impl Into<crate::SharedModule>,
) { ) {
if self.imports.is_none() { self.imports
self.imports = Some(crate::StaticVec::new_const().into()); .get_or_insert_with(|| crate::StaticVec::new_const().into())
self.modules = Some(crate::StaticVec::new_const().into()); .push(name.into());
}
self.imports.as_mut().unwrap().push(name.into()); self.modules
self.modules.as_mut().unwrap().push(module.into()); .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. /// 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 { impl<K: Into<ImmutableString>, M: Into<crate::SharedModule>> Extend<(K, M)> for GlobalRuntimeState {
#[inline] #[inline]
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
if self.imports.is_none() { let imports = self
self.imports = Some(crate::StaticVec::new_const().into()); .imports
self.modules = Some(crate::StaticVec::new_const().into()); .get_or_insert_with(|| crate::StaticVec::new_const().into());
} let modules = self
.modules
let imports = self.imports.as_mut().unwrap(); .get_or_insert_with(|| crate::StaticVec::new_const().into());
let modules = self.modules.as_mut().unwrap();
for (k, m) in iter { for (k, m) in iter {
imports.push(k.into()); imports.push(k.into());

View File

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

View File

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

View File

@ -353,6 +353,43 @@ impl Scope<'_> {
self.values.push(value); self.values.push(value);
self 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. /// Truncate (rewind) the [`Scope`] to a previous size.
/// ///
/// # Example /// # Example