From cefe3f1715588199441585eadef29d6b01653c81 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 24 Nov 2022 22:25:19 +0800 Subject: [PATCH] Reduce Module size. --- src/api/deprecated.rs | 22 ++- src/engine.rs | 2 +- src/eval/global_state.rs | 26 +-- src/module/mod.rs | 336 +++++++++++++++++++++------------------ src/parser.rs | 146 ++++++++--------- src/types/scope.rs | 37 +++++ 6 files changed, 322 insertions(+), 247 deletions(-) diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index b1bb63a0..1549335b 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -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() + } +} diff --git a/src/engine.rs b/src/engine.rs index 03761b90..a17fb24d 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -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()); diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index bc2ae8d0..98ba8263 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -173,12 +173,13 @@ impl GlobalRuntimeState { name: impl Into, module: impl Into, ) { - 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, M: Into> Extend<(K, M)> for GlobalRuntimeState { #[inline] fn extend>(&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()); diff --git a/src/module/mod.rs b/src/module/mod.rs index 68fe9f2e..157bdfe6 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -177,26 +177,26 @@ pub struct Module { id: Option, /// Module documentation. #[cfg(feature = "metadata")] - doc: crate::SmartString, + doc: Option, /// Custom types. - custom_types: Option, + custom_types: Option>, /// Sub-modules. - modules: Option>, + modules: Option>>, /// [`Module`] variables. - variables: Option>, + variables: Option>>, /// Flattened collection of all [`Module`] variables, including those in sub-modules. - all_variables: Option>, + all_variables: Option>>, /// Functions (both native Rust and scripted). - functions: StraightHashMap, + functions: Option>>, /// Flattened collection of all functions, native Rust and scripted. /// including those in sub-modules. - all_functions: Option>, + all_functions: Option>>, /// 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>>, + type_iterators: Option>>>, /// Flattened collection of iterator functions, including those in sub-modules. - all_type_iterators: Option>>, + all_type_iterators: Option>>>, /// 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::("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) -> &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(&mut self, name: &str) -> &mut Self { self.custom_types - .get_or_insert_with(CustomTypesCollection::new) + .get_or_insert_with(|| CustomTypesCollection::new().into()) .add_type::(name); self } @@ -501,7 +488,7 @@ impl Module { name: impl Into, ) -> &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, num_params: usize, ) -> Option<&Shared> { - 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 { - 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; diff --git a/src/parser.rs b/src/parser.rs index f3127928..d1882614 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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)), diff --git a/src/types/scope.rs b/src/types/scope.rs index e217a698..639195cb 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -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