diff --git a/src/ast.rs b/src/ast.rs index 22bb7426..a3d143ae 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1301,18 +1301,41 @@ pub struct OpAssignment { /// _(INTERNALS)_ An set of function call hashes. /// Exported under the `internals` feature only. /// +/// Two separate hashes are pre-calculated because of the following pattern: +/// +/// ```rust,no_run +/// func(a, b, c); // Native: func(a, b, c) - 3 parameters +/// // Script: func(a, b, c) - 3 parameters +/// +/// a.func(b, c); // Native: func(&mut a, b, c) - 3 parameters +/// // Script: func(b, c) - 2 parameters +/// ``` +/// +/// For normal function calls, the native hash equals the script hash. +/// For method-style calls, the script hash contains one fewer parameter. +/// +/// Function call hashes are used in the following manner: +/// +/// * First, the script hash is tried, which contains only the called function's name plus the +/// of parameters. +/// +/// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is +/// then used to search for a native function. +/// In other words, a native function call hash always contains the called function's name plus +/// the types of the arguments. This is to due to possible function overloading for different parameter types. +/// /// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] -pub struct FnHash { +pub struct FnCallHash { /// Pre-calculated hash for a script-defined function ([`None`] if native functions only). - script: Option, + pub script: Option, /// Pre-calculated hash for a native Rust function with no parameter types. - native: u64, + pub native: u64, } -impl fmt::Debug for FnHash { +impl fmt::Debug for FnCallHash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(script) = self.script { if script == self.native { @@ -1326,8 +1349,8 @@ impl fmt::Debug for FnHash { } } -impl FnHash { - /// Create a [`FnHash`] with only the native Rust hash. +impl FnCallHash { + /// Create a [`FnCallHash`] with only the native Rust hash. #[inline(always)] pub fn from_native(hash: u64) -> Self { Self { @@ -1335,7 +1358,7 @@ impl FnHash { native: hash, } } - /// Create a [`FnHash`] with both native Rust and script function hashes set to the same value. + /// Create a [`FnCallHash`] with both native Rust and script function hashes set to the same value. #[inline(always)] pub fn from_script(hash: u64) -> Self { Self { @@ -1343,7 +1366,7 @@ impl FnHash { native: hash, } } - /// Create a [`FnHash`] with both native Rust and script function hashes. + /// Create a [`FnCallHash`] with both native Rust and script function hashes. #[inline(always)] pub fn from_script_and_native(script: u64, native: u64) -> Self { Self { @@ -1351,21 +1374,21 @@ impl FnHash { native, } } - /// Is this [`FnHash`] native Rust only? + /// Is this [`FnCallHash`] native Rust only? #[inline(always)] pub fn is_native_only(&self) -> bool { self.script.is_none() } - /// Get the script function hash from this [`FnHash`]. + /// Get the script function hash from this [`FnCallHash`]. /// /// # Panics /// - /// Panics if the [`FnHash`] is native Rust only. + /// Panics if the [`FnCallHash`] is native Rust only. #[inline(always)] pub fn script_hash(&self) -> u64 { self.script.unwrap() } - /// Get the naive Rust function hash from this [`FnHash`]. + /// Get the naive Rust function hash from this [`FnCallHash`]. #[inline(always)] pub fn native_hash(&self) -> u64 { self.native @@ -1381,7 +1404,7 @@ impl FnHash { #[derive(Debug, Clone, Default, Hash)] pub struct FnCallExpr { /// Pre-calculated hash. - pub hash: FnHash, + pub hash: FnCallHash, /// Does this function call capture the parent scope? pub capture: bool, /// List of function call arguments. diff --git a/src/engine.rs b/src/engine.rs index 28b09837..4061431c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation [`Engine`]. -use crate::ast::{Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, Stmt, StmtBlock}; +use crate::ast::{Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType, Stmt, StmtBlock}; use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::fn_native::{ CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback, @@ -527,10 +527,8 @@ pub struct State { /// Embedded module resolver. #[cfg(not(feature = "no_module"))] pub resolver: Option>, - /// function resolution cache. - fn_resolution_caches: StaticVec, - /// Free resolution caches. - fn_resolution_caches_free_list: Vec, + /// Function resolution cache and free list. + fn_resolution_caches: (StaticVec, Vec), } impl State { @@ -541,20 +539,19 @@ impl State { } /// Get a mutable reference to the current function resolution cache. pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { - if self.fn_resolution_caches.is_empty() { + if self.fn_resolution_caches.0.is_empty() { self.fn_resolution_caches - .push(HashMap::with_capacity_and_hasher(16, StraightHasherBuilder)); + .0 + .push(HashMap::with_capacity_and_hasher(64, StraightHasherBuilder)); } - self.fn_resolution_caches.last_mut().unwrap() + self.fn_resolution_caches.0.last_mut().unwrap() } /// Push an empty function resolution cache onto the stack and make it current. #[allow(dead_code)] pub fn push_fn_resolution_cache(&mut self) { - self.fn_resolution_caches.push( - self.fn_resolution_caches_free_list - .pop() - .unwrap_or_default(), - ); + self.fn_resolution_caches + .0 + .push(self.fn_resolution_caches.1.pop().unwrap_or_default()); } /// Remove the current function resolution cache from the stack and make the last one current. /// @@ -562,9 +559,9 @@ impl State { /// /// Panics if there are no more function resolution cache in the stack. pub fn pop_fn_resolution_cache(&mut self) { - let mut cache = self.fn_resolution_caches.pop().unwrap(); + let mut cache = self.fn_resolution_caches.0.pop().unwrap(); cache.clear(); - self.fn_resolution_caches_free_list.push(cache); + self.fn_resolution_caches.1.push(cache); } } @@ -1143,7 +1140,7 @@ impl Engine { let ((_, val_pos), _) = new_val; let hash_set = - FnHash::from_native(calc_fn_hash(empty(), FN_IDX_SET, 3)); + FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_SET, 3)); let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0]; self.exec_fn_call( @@ -1221,7 +1218,7 @@ impl Engine { // xxx.id = ??? Expr::Property(x) if new_val.is_some() => { let (_, (setter, hash_set), Ident { pos, .. }) = x.as_ref(); - let hash = FnHash::from_native(*hash_set); + let hash = FnCallHash::from_native(*hash_set); let mut new_val = new_val; let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0]; self.exec_fn_call( @@ -1233,7 +1230,7 @@ impl Engine { // xxx.id Expr::Property(x) => { let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref(); - let hash = FnHash::from_native(*hash_get); + let hash = FnCallHash::from_native(*hash_get); let mut args = [target_val]; self.exec_fn_call( mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None, @@ -1282,8 +1279,8 @@ impl Engine { Expr::Property(p) => { let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = p.as_ref(); - let hash_get = FnHash::from_native(*hash_get); - let hash_set = FnHash::from_native(*hash_set); + let hash_get = FnCallHash::from_native(*hash_get); + let hash_set = FnCallHash::from_native(*hash_set); let arg_values = &mut [target_val, &mut Default::default()]; let args = &mut arg_values[..1]; let (mut val, updated) = self.exec_fn_call( @@ -1615,7 +1612,7 @@ impl Engine { let type_name = target.type_name(); let mut idx = idx; let args = &mut [target, &mut idx]; - let hash_get = FnHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2)); + let hash_get = FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2)); self.exec_fn_call( _mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None, _level, diff --git a/src/fn_call.rs b/src/fn_call.rs index 2f683808..c07e5c13 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1,6 +1,6 @@ //! Implement function-calling mechanism for [`Engine`]. -use crate::ast::FnHash; +use crate::ast::FnCallHash; use crate::engine::{ FnResolutionCacheEntry, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, @@ -627,7 +627,7 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - hash: FnHash, + hash: FnCallHash, args: &mut FnCallArgs, is_ref: bool, _is_method: bool, @@ -893,7 +893,7 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - mut hash: FnHash, + mut hash: FnCallHash, target: &mut crate::engine::Target, (call_args, call_arg_positions): &mut (StaticVec, StaticVec), pos: Position, @@ -913,7 +913,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hashes - let new_hash = FnHash::from_script(calc_fn_hash(empty(), fn_name, args_len)); + let new_hash = FnCallHash::from_script(calc_fn_hash(empty(), fn_name, args_len)); // Arguments are passed as-is, adding the curried arguments let mut curry = fn_ptr.curry().iter().cloned().collect::>(); let mut arg_values = curry @@ -949,7 +949,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hash - let new_hash = FnHash::from_script_and_native( + let new_hash = FnCallHash::from_script_and_native( calc_fn_hash(empty(), fn_name, args_len), calc_fn_hash(empty(), fn_name, args_len + 1), ); @@ -1024,7 +1024,7 @@ impl Engine { call_arg_positions.insert(i, Position::NONE); }); // Recalculate the hash based on the new function name and new arguments - hash = FnHash::from_script_and_native( + hash = FnCallHash::from_script_and_native( calc_fn_hash(empty(), fn_name, call_args.len()), calc_fn_hash(empty(), fn_name, call_args.len() + 1), ); @@ -1062,7 +1062,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, args_expr: &[Expr], - mut hash: FnHash, + mut hash: FnCallHash, pos: Position, capture_scope: bool, level: usize, @@ -1101,9 +1101,9 @@ impl Engine { // Recalculate hash let args_len = args_expr.len() + curry.len(); hash = if !hash.is_native_only() { - FnHash::from_script(calc_fn_hash(empty(), name, args_len)) + FnCallHash::from_script(calc_fn_hash(empty(), name, args_len)) } else { - FnHash::from_native(calc_fn_hash(empty(), name, args_len)) + FnCallHash::from_native(calc_fn_hash(empty(), name, args_len)) }; } // Handle Fn() diff --git a/src/fn_native.rs b/src/fn_native.rs index f4d41ae4..12cb94b6 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,6 +1,6 @@ //! Module defining interfaces to native-Rust functions. -use crate::ast::{FnAccess, FnHash}; +use crate::ast::{FnAccess, FnCallHash}; use crate::engine::Imports; use crate::plugin::PluginFunction; use crate::stdlib::{ @@ -190,12 +190,12 @@ impl<'a> NativeCallContext<'a> { args: &mut [&mut Dynamic], ) -> RhaiResult { let hash = if is_method { - FnHash::from_script_and_native( + FnCallHash::from_script_and_native( calc_fn_hash(empty(), fn_name, args.len() - 1), calc_fn_hash(empty(), fn_name, args.len()), ) } else { - FnHash::from_script(calc_fn_hash(empty(), fn_name, args.len())) + FnCallHash::from_script(calc_fn_hash(empty(), fn_name, args.len())) }; self.engine() diff --git a/src/lib.rs b/src/lib.rs index 215db356..0b34b4af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,8 +191,8 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni #[cfg(feature = "internals")] #[deprecated = "this type is volatile and may change"] pub use ast::{ - ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnHash, Ident, OpAssignment, - ReturnType, ScriptFnDef, Stmt, StmtBlock, + ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHash, Ident, + OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock, }; #[cfg(feature = "internals")] @@ -207,15 +207,72 @@ pub use engine::Limits; #[deprecated = "this type is volatile and may change"] pub use module::NamespaceRef; -/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), -/// which is a specialized [`Vec`] backed by a small, fixed-size array when there are <= 4 items stored. -/// Exported under the `internals` feature only. +/// Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), which is a +/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are <= 4 items stored. +/// +/// # Background +/// +/// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec` +/// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy." +/// +/// And the Lord did grin, and the people did feast upon the lambs and sloths and carp and anchovies +/// and orangutans and breakfast cereals and fruit bats and large chu... +/// +/// And the Lord spake, saying, "First shalt thou depend on the [`smallvec`](https://crates.io/crates/smallvec) crate. +/// Then, shalt thou keep four inline. No more. No less. Four shalt be the number thou shalt keep inline, +/// and the number to keep inline shalt be four. Five shalt thou not keep inline, nor either keep inline +/// thou two or three, excepting that thou then proceed to four. Six is right out. Once the number four, +/// being the forth number, be reached, then, lobbest thou thy `SmallVec` towards thy heap, who, +/// being slow and cache-naughty in My sight, shall snuff it." +/// +/// # Explanation on the Number Four +/// +/// `StaticVec` is used frequently to keep small lists of items in inline (non-heap) storage in +/// order to improve cache friendliness and reduce indirections. +/// +/// The number 4, other than being the holy number, is carefully chosen for a balance between +/// storage space and reduce allocations. That is because most function calls (and most functions, +/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a +/// large number of external variables. +/// +/// In addition, most scripts blocks either contain many statements, or just a few lines; +/// most scripts load fewer than 5 external modules; most module paths contain fewer than 5 levels +/// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long). #[cfg(not(feature = "internals"))] type StaticVec = smallvec::SmallVec<[T; 4]>; -/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), -/// which is a specialized [`Vec`] backed by a small, fixed-size array when there are <= 4 items stored. +/// _(INTERNALS)_ Alias to [`smallvec`](https://crates.io/crates/smallvec), which is a specialized +/// [`Vec`] backed by a small, inline, fixed-size array when there are <= 4 items stored. /// Exported under the `internals` feature only. +/// +/// # Background +/// +/// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec` +/// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy." +/// +/// And the Lord did grin, and the people did feast upon the lambs and sloths and carp and anchovies +/// and orangutans and breakfast cereals and fruit bats and large chu... +/// +/// And the Lord spake, saying, "First shalt thou depend on the [`smallvec`](https://crates.io/crates/smallvec) crate. +/// Then, shalt thou keep four inline. No more. No less. Four shalt be the number thou shalt keep inline, +/// and the number to keep inline shalt be four. Five shalt thou not keep inline, nor either keep inline +/// thou two or three, excepting that thou then proceed to four. Six is right out. Once the number four, +/// being the forth number, be reached, then, lobbest thou thy `SmallVec` towards thy heap, who, +/// being slow and cache-naughty in My sight, shall snuff it." +/// +/// # Explanation on the Number Four +/// +/// `StaticVec` is used frequently to keep small lists of items in inline (non-heap) storage in +/// order to improve cache friendliness and reduce indirections. +/// +/// The number 4, other than being the holy number, is carefully chosen for a balance between +/// storage space and reduce allocations. That is because most function calls (and most functions, +/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a +/// large number of external variables. +/// +/// In addition, most scripts blocks either contain many statements, or just a few lines; +/// most scripts load fewer than 5 external modules; most module paths contain fewer than 5 levels +/// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long). #[cfg(feature = "internals")] pub type StaticVec = smallvec::SmallVec<[T; 4]>; diff --git a/src/module/mod.rs b/src/module/mod.rs index 626a5e65..2ca01980 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1398,12 +1398,14 @@ impl Module { scope.into_iter().for_each(|(_, value, mut aliases)| { // Variables with an alias left in the scope become module variables - if aliases.len() > 1 { - aliases.into_iter().for_each(|alias| { + match aliases.len() { + 0 => (), + 1 => { + module.variables.insert(aliases.pop().unwrap(), value); + } + _ => aliases.into_iter().for_each(|alias| { module.variables.insert(alias, value.clone()); - }); - } else if aliases.len() == 1 { - module.variables.insert(aliases.pop().unwrap(), value); + }), } }); diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 3a6e489d..c6eab910 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -108,8 +108,8 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { list.push(make_metadata(dict, Some(namespace.clone()), f).into()) }); module.iter_sub_modules().for_each(|(ns, m)| { - let ns: ImmutableString = format!("{}::{}", namespace, ns).into(); - scan_module(list, dict, ns, m.as_ref()) + let ns = format!("{}::{}", namespace, ns); + scan_module(list, dict, ns.into(), m.as_ref()) }); } diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index f54af86c..673b9be3 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -29,15 +29,11 @@ mod map_functions { } #[rhai_fn(name = "mixin", name = "+=")] pub fn mixin(map: &mut Map, map2: Map) { - map2.into_iter().for_each(|(key, value)| { - map.insert(key, value); - }); + map.extend(map2.into_iter()); } #[rhai_fn(name = "+")] pub fn merge(mut map: Map, map2: Map) -> Map { - map2.into_iter().for_each(|(key, value)| { - map.insert(key, value); - }); + map.extend(map2.into_iter()); map } pub fn fill_with(map: &mut Map, map2: Map) { diff --git a/src/parser.rs b/src/parser.rs index 12a714a8..21ae4114 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,8 +1,8 @@ //! Main module defining the lexer and parser. use crate::ast::{ - BinaryExpr, CustomExpr, Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, ScriptFnDef, - Stmt, StmtBlock, + BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType, + ScriptFnDef, Stmt, StmtBlock, }; use crate::dynamic::{AccessMode, Union}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; @@ -359,9 +359,9 @@ fn parse_fn_call( capture, namespace, hash: if is_valid_identifier(id.chars()) { - FnHash::from_script(hash) + FnCallHash::from_script(hash) } else { - FnHash::from_native(hash) + FnCallHash::from_native(hash) }, args, ..Default::default() @@ -402,9 +402,9 @@ fn parse_fn_call( capture, namespace, hash: if is_valid_identifier(id.chars()) { - FnHash::from_script(hash) + FnCallHash::from_script(hash) } else { - FnHash::from_native(hash) + FnCallHash::from_native(hash) }, args, ..Default::default() @@ -1291,7 +1291,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)), + hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)), args, ..Default::default() }), @@ -1318,7 +1318,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)), + hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)), args, ..Default::default() }), @@ -1339,7 +1339,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)), + hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)), args, ..Default::default() }), @@ -1538,7 +1538,7 @@ fn make_dot_expr( } Expr::FnCall(mut func, func_pos) => { // Recalculate hash - func.hash = FnHash::from_script_and_native( + func.hash = FnCallHash::from_script_and_native( calc_fn_hash(empty(), &func.name, func.args.len()), calc_fn_hash(empty(), &func.name, func.args.len() + 1), ); @@ -1594,7 +1594,7 @@ fn make_dot_expr( // lhs.func(...) (lhs, Expr::FnCall(mut func, func_pos)) => { // Recalculate hash - func.hash = FnHash::from_script_and_native( + func.hash = FnCallHash::from_script_and_native( calc_fn_hash(empty(), &func.name, func.args.len()), calc_fn_hash(empty(), &func.name, func.args.len() + 1), ); @@ -1682,7 +1682,7 @@ fn parse_binary_op( let op_base = FnCallExpr { name: op, - hash: FnHash::from_native(hash), + hash: FnCallHash::from_native(hash), capture: false, ..Default::default() }; @@ -1747,7 +1747,7 @@ fn parse_binary_op( let hash = calc_fn_hash(empty(), OP_CONTAINS, 2); Expr::FnCall( Box::new(FnCallExpr { - hash: FnHash::from_script(hash), + hash: FnCallHash::from_script(hash), args, name: OP_CONTAINS.into(), ..op_base @@ -1768,9 +1768,9 @@ fn parse_binary_op( Expr::FnCall( Box::new(FnCallExpr { hash: if is_valid_identifier(s.chars()) { - FnHash::from_script(hash) + FnCallHash::from_script(hash) } else { - FnHash::from_native(hash) + FnCallHash::from_native(hash) }, args, ..op_base @@ -2783,7 +2783,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po let expr = Expr::FnCall( Box::new(FnCallExpr { name: curry_func.into(), - hash: FnHash::from_native(calc_fn_hash(empty(), curry_func, num_externals + 1)), + hash: FnCallHash::from_native(calc_fn_hash(empty(), curry_func, num_externals + 1)), args, ..Default::default() }), @@ -2884,7 +2884,7 @@ fn parse_anon_fn( // Create unique function name by hashing the script body plus the parameters. let hasher = &mut get_hasher(); - params.iter().for_each(|p| p.as_str().hash(hasher)); + params.iter().for_each(|p| p.hash(hasher)); body.hash(hasher); let hash = hasher.finish(); diff --git a/src/scope.rs b/src/scope.rs index 2fa8f235..4ae73cbd 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -434,10 +434,10 @@ impl<'a> Scope<'a> { .iter() .enumerate() .rev() - .for_each(|(index, (name, alias))| { + .for_each(|(i, (name, alias))| { if !entries.names.iter().any(|(key, _)| key == name) { entries.names.push((name.clone(), alias.clone())); - entries.values.push(self.values[index].clone()); + entries.values.push(self.values[i].clone()); } }); diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 05a709a9..3f8c9bed 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -228,8 +228,8 @@ impl Engine { if include_global { self.global_modules .iter() - .flat_map(|m| m.iter_fn().map(|f| f.into())) - .for_each(|info| global.functions.push(info)); + .flat_map(|m| m.iter_fn()) + .for_each(|f| global.functions.push(f.into())); } self.global_sub_modules.iter().for_each(|(name, m)| { @@ -238,13 +238,11 @@ impl Engine { self.global_namespace .iter_fn() - .map(|f| f.into()) - .for_each(|info| global.functions.push(info)); + .for_each(|f| global.functions.push(f.into())); #[cfg(not(feature = "no_function"))] _ast.iter_functions() - .map(|f| f.into()) - .for_each(|info| global.functions.push(info)); + .for_each(|f| global.functions.push(f.into())); global.functions.sort(); diff --git a/src/utils.rs b/src/utils.rs index dd768752..3bdc9250 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -72,17 +72,16 @@ pub fn get_hasher() -> ahash::AHasher { /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline(always)] -pub fn calc_fn_hash<'a>( - mut modules: impl Iterator, - fn_name: &str, - num: usize, -) -> u64 { +pub fn calc_fn_hash<'a>(modules: impl Iterator, fn_name: &str, num: usize) -> u64 { let s = &mut get_hasher(); - // Hash a boolean indicating whether the hash is namespace-qualified. - modules.next().is_some().hash(s); // We always skip the first module - modules.for_each(|m| m.hash(s)); + let mut len = 0; + modules + .inspect(|_| len += 1) + .skip(1) + .for_each(|m| m.hash(s)); + len.hash(s); fn_name.hash(s); num.hash(s); s.finish() @@ -96,10 +95,7 @@ pub fn calc_fn_hash<'a>( pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { let s = &mut get_hasher(); let mut len = 0; - params.for_each(|t| { - t.hash(s); - len += 1; - }); + params.inspect(|_| len += 1).for_each(|t| t.hash(s)); len.hash(s); s.finish() }