diff --git a/src/engine.rs b/src/engine.rs index 85c0c9f4..d45d1099 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -12,7 +12,7 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::token::Position; -use crate::utils::{StaticVec, EMPTY_TYPE_ID}; +use crate::utils::StaticVec; #[cfg(not(feature = "no_module"))] use crate::module::{resolvers, ModuleRef, ModuleResolver}; @@ -25,7 +25,7 @@ use crate::stdlib::{ boxed::Box, collections::HashMap, format, - iter::{empty, once, repeat}, + iter::{empty, once}, mem, num::NonZeroUsize, ops::{Deref, DerefMut}, @@ -215,8 +215,7 @@ impl<'a> State<'a> { /// Since script-defined functions have `Dynamic` parameters, functions with the same name /// and number of parameters are considered equivalent. /// -/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_hash` -/// with dummy parameter types `EMPTY_TYPE_ID()` repeated the correct number of times. +/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_hash`. #[derive(Debug, Clone, Default)] pub struct FunctionsLib(HashMap); @@ -226,9 +225,8 @@ impl FunctionsLib { FunctionsLib( vec.into_iter() .map(|fn_def| { - // Qualifiers (none) + function name + placeholders (one for each parameter). - let args_iter = repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()); - let hash = calc_fn_hash(empty(), &fn_def.name, args_iter); + // Qualifiers (none) + function name + number of arguments. + let hash = calc_fn_hash(empty(), &fn_def.name, fn_def.params.len(), empty()); (hash, fn_def.into()) }) .collect(), @@ -253,8 +251,8 @@ impl FunctionsLib { params: usize, public_only: bool, ) -> Option<&FnDef> { - // Qualifiers (none) + function name + placeholders (one for each parameter). - let hash_fn_def = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params)); + // Qualifiers (none) + function name + number of arguments. + let hash_fn_def = calc_fn_hash(empty(), name, params, empty()); let fn_def = self.get_function(hash_fn_def); match fn_def.as_ref().map(|f| f.access) { @@ -873,8 +871,13 @@ impl Engine { pos: Position, level: usize, ) -> Result<(Dynamic, bool), Box> { - // Qualifiers (none) + function name + argument `TypeId`'s. - let hash_fn = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id())); + // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. + let hash_fn = calc_fn_hash( + empty(), + fn_name, + args.len(), + args.iter().map(|a| a.type_id()), + ); let hashes = (hash_fn, hash_fn_def); match fn_name { @@ -1322,7 +1325,7 @@ impl Engine { Dynamic(Union::Array(mut rhs_value)) => { let op = "=="; let def_value = false.into(); - let hash_fn_def = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2)); + let hash_fn_def = calc_fn_hash(empty(), op, 2, empty()); // Call the `==` operator to compare each value for value in rhs_value.iter_mut() { @@ -1331,7 +1334,8 @@ impl Engine { let pos = rhs.position(); // Qualifiers (none) + function name + argument `TypeId`'s. - let hash_fn = calc_fn_hash(empty(), op, args.iter().map(|a| a.type_id())); + let hash_fn = + calc_fn_hash(empty(), op, args.len(), args.iter().map(|a| a.type_id())); let hashes = (hash_fn, hash_fn_def); let (r, _) = self @@ -1486,7 +1490,7 @@ impl Engine { let mut args: StaticVec<_> = arg_values.iter_mut().collect(); if name == KEYWORD_EVAL && args.len() == 1 && args.get(0).is::() { - let hash_fn = calc_fn_hash(empty(), name, once(TypeId::of::())); + let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::())); if !self.has_override(state, (hash_fn, *hash_fn_def)) { // eval - only in function call style @@ -1547,11 +1551,11 @@ impl Engine { // Rust functions are indexed in two steps: // 1) Calculate a hash in a similar manner to script-defined functions, - // i.e. qualifiers + function name + dummy parameter types (one for each parameter). - // 2) Calculate a second hash with no qualifiers, empty function name, and - // the actual list of parameter `TypeId`'.s + // i.e. qualifiers + function name + number of arguments. + // 2) Calculate a second hash with no qualifiers, empty function name, + // zero number of arguments, and the actual list of argument `TypeId`'.s let hash_fn_args = - calc_fn_hash(empty(), "", args.iter().map(|a| a.type_id())); + calc_fn_hash(empty(), "", 0, args.iter().map(|a| a.type_id())); // 3) The final hash is the XOR of the two hashes. let hash_fn_native = *hash_fn_def ^ hash_fn_args; diff --git a/src/module.rs b/src/module.rs index 06d0cbbd..59a0d0f1 100644 --- a/src/module.rs +++ b/src/module.rs @@ -8,18 +8,22 @@ use crate::fn_native::{ CallableFunction::{Method, Pure}, FnCallArgs, IteratorFn, SharedFunction, }; -use crate::parser::{FnAccess, AST}; +use crate::parser::{ + FnAccess, + FnAccess::{Private, Public}, + AST, +}; use crate::result::EvalAltResult; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::token::{Position, Token}; -use crate::utils::{StaticVec, EMPTY_TYPE_ID}; +use crate::utils::StaticVec; use crate::stdlib::{ any::TypeId, boxed::Box, collections::HashMap, fmt, - iter::{empty, repeat}, + iter::empty, mem, num::NonZeroUsize, ops::{Deref, DerefMut}, @@ -30,9 +34,6 @@ use crate::stdlib::{ vec::Vec, }; -/// Default function access mode. -const DEF_ACCESS: FnAccess = FnAccess::Public; - /// Return type of module-level Rust function. pub type FuncReturn = Result>; @@ -281,7 +282,7 @@ impl Module { params: &[TypeId], func: CallableFunction, ) -> u64 { - let hash_fn = calc_fn_hash(empty(), &name, params.iter().cloned()); + let hash_fn = calc_fn_hash(empty(), &name, params.len(), params.iter().cloned()); let params = params.into_iter().cloned().collect(); @@ -312,7 +313,7 @@ impl Module { ) -> u64 { let f = move |_: &mut FnCallArgs| func().map(Dynamic::from); let arg_types = []; - self.set_fn(name.into(), DEF_ACCESS, &arg_types, Pure(Box::new(f))) + self.set_fn(name.into(), Public, &arg_types, Pure(Box::new(f))) } /// Set a Rust function taking one parameter into the module, returning a hash key. @@ -337,7 +338,7 @@ impl Module { let f = move |args: &mut FnCallArgs| func(mem::take(args[0]).cast::()).map(Dynamic::from); let arg_types = [TypeId::of::()]; - self.set_fn(name.into(), DEF_ACCESS, &arg_types, Pure(Box::new(f))) + self.set_fn(name.into(), Public, &arg_types, Pure(Box::new(f))) } /// Set a Rust function taking one mutable parameter into the module, returning a hash key. @@ -363,7 +364,7 @@ impl Module { func(args[0].downcast_mut::().unwrap()).map(Dynamic::from) }; let arg_types = [TypeId::of::()]; - self.set_fn(name.into(), DEF_ACCESS, &arg_types, Method(Box::new(f))) + self.set_fn(name.into(), Public, &arg_types, Method(Box::new(f))) } /// Set a Rust function taking two parameters into the module, returning a hash key. @@ -394,7 +395,7 @@ impl Module { func(a, b).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::()]; - self.set_fn(name.into(), DEF_ACCESS, &arg_types, Pure(Box::new(f))) + self.set_fn(name.into(), Public, &arg_types, Pure(Box::new(f))) } /// Set a Rust function taking two parameters (the first one mutable) into the module, @@ -429,7 +430,7 @@ impl Module { func(a, b).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::()]; - self.set_fn(name.into(), DEF_ACCESS, &arg_types, Method(Box::new(f))) + self.set_fn(name.into(), Public, &arg_types, Method(Box::new(f))) } /// Set a Rust function taking three parameters into the module, returning a hash key. @@ -467,7 +468,7 @@ impl Module { func(a, b, c).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; - self.set_fn(name.into(), DEF_ACCESS, &arg_types, Pure(Box::new(f))) + self.set_fn(name.into(), Public, &arg_types, Pure(Box::new(f))) } /// Set a Rust function taking three parameters (the first one mutable) into the module, @@ -506,7 +507,7 @@ impl Module { func(a, b, c).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; - self.set_fn(name.into(), DEF_ACCESS, &arg_types, Method(Box::new(f))) + self.set_fn(name.into(), Public, &arg_types, Method(Box::new(f))) } /// Get a Rust function. @@ -617,27 +618,24 @@ impl Module { // Index all variables for (var_name, value) in &module.variables { // Qualifiers + variable name - let hash_var = calc_fn_hash(qualifiers.iter().map(|&v| v), var_name, empty()); + let hash_var = calc_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0, empty()); variables.push((hash_var, value.clone())); } // Index all Rust functions for (name, access, params, func) in module.functions.values() { match access { // Private functions are not exported - FnAccess::Private => continue, - FnAccess::Public => (), + Private => continue, + Public => (), } // Rust functions are indexed in two steps: // 1) Calculate a hash in a similar manner to script-defined functions, - // i.e. qualifiers + function name + dummy parameter types (one for each parameter). - let hash_fn_def = calc_fn_hash( - qualifiers.iter().map(|&v| v), - name, - repeat(EMPTY_TYPE_ID()).take(params.len()), - ); - // 2) Calculate a second hash with no qualifiers, empty function name, and - // the actual list of parameter `TypeId`'.s - let hash_fn_args = calc_fn_hash(empty(), "", params.iter().cloned()); + // i.e. qualifiers + function name + number of arguments. + let hash_fn_def = + calc_fn_hash(qualifiers.iter().map(|&v| v), name, params.len(), empty()); + // 2) Calculate a second hash with no qualifiers, empty function name, + // zero number of arguments, and the actual list of argument `TypeId`'.s + let hash_fn_args = calc_fn_hash(empty(), "", 0, params.iter().cloned()); // 3) The final hash is the XOR of the two hashes. let hash_fn_native = hash_fn_def ^ hash_fn_args; @@ -647,14 +645,15 @@ impl Module { for fn_def in module.fn_lib.values() { match fn_def.access { // Private functions are not exported - FnAccess::Private => continue, - DEF_ACCESS => (), + Private => continue, + Public => (), } - // Qualifiers + function name + placeholders (one for each parameter) + // Qualifiers + function name + number of arguments. let hash_fn_def = calc_fn_hash( qualifiers.iter().map(|&v| v), &fn_def.name, - repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()), + fn_def.params.len(), + empty(), ); functions.push((hash_fn_def, CallableFunction::Script(fn_def.clone()).into())); } diff --git a/src/optimize.rs b/src/optimize.rs index d320d4bf..1bba1196 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -119,7 +119,12 @@ fn call_fn( pos: Position, ) -> Result, Box> { // Search built-in's and external functions - let hash_fn = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id())); + let hash_fn = calc_fn_hash( + empty(), + fn_name, + args.len(), + args.iter().map(|a| a.type_id()), + ); global_module .get_fn(hash_fn) diff --git a/src/parser.rs b/src/parser.rs index 39fa6678..1c013666 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -7,7 +7,7 @@ use crate::error::{LexError, ParseError, ParseErrorType}; use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::token::{Position, Token, TokenIterator}; -use crate::utils::{StaticVec, EMPTY_TYPE_ID}; +use crate::utils::StaticVec; #[cfg(not(feature = "no_module"))] use crate::module::ModuleRef; @@ -22,7 +22,7 @@ use crate::stdlib::{ char, collections::HashMap, format, - iter::{empty, repeat, Peekable}, + iter::{empty, Peekable}, num::NonZeroUsize, ops::{Add, Deref, DerefMut}, rc::Rc, @@ -767,13 +767,14 @@ fn parse_call_expr<'a>( // Rust functions are indexed in two steps: // 1) Calculate a hash in a similar manner to script-defined functions, - // i.e. qualifiers + function name + no parameters. - // 2) Calculate a second hash with no qualifiers, empty function name, and - // the actual list of parameter `TypeId`'.s + // i.e. qualifiers + function name + number of arguments. + // 2) Calculate a second hash with no qualifiers, empty function name, + // zero number of arguments, and the actual list of argument `TypeId`'s. // 3) The final hash is the XOR of the two hashes. - calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty()) + let qualifiers = modules.iter().map(|(m, _)| m.as_str()); + calc_fn_hash(qualifiers, &id, 0, empty()) } else { - calc_fn_hash(empty(), &id, empty()) + calc_fn_hash(empty(), &id, 0, empty()) } }; // Qualifiers (none) + function name + no parameters. @@ -799,7 +800,6 @@ fn parse_call_expr<'a>( // id(...args) (Token::RightParen, _) => { eat_token(input, Token::RightParen); - let args_iter = repeat(EMPTY_TYPE_ID()).take(args.len()); #[cfg(not(feature = "no_module"))] let hash_fn_def = { @@ -808,13 +808,14 @@ fn parse_call_expr<'a>( // Rust functions are indexed in two steps: // 1) Calculate a hash in a similar manner to script-defined functions, - // i.e. qualifiers + function name + dummy parameter types (one for each parameter). - // 2) Calculate a second hash with no qualifiers, empty function name, and - // the actual list of parameter `TypeId`'.s + // i.e. qualifiers + function name + number of arguments. + // 2) Calculate a second hash with no qualifiers, empty function name, + // zero number of arguments, and the actual list of argument `TypeId`'s. // 3) The final hash is the XOR of the two hashes. - calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, args_iter) + let qualifiers = modules.iter().map(|(m, _)| m.as_str()); + calc_fn_hash(qualifiers, &id, args.len(), empty()) } else { - calc_fn_hash(empty(), &id, args_iter) + calc_fn_hash(empty(), &id, args.len(), empty()) } }; // Qualifiers (none) + function name + dummy parameter types (one for each parameter). @@ -1297,7 +1298,7 @@ fn parse_primary<'a>( let modules = modules.as_mut().unwrap(); // Qualifiers + variable name - *hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, empty()); + *hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0, empty()); modules.set_index(state.find_module(&modules.get(0).0)); } _ => (), @@ -1362,7 +1363,7 @@ fn parse_unary<'a>( // Call negative function expr => { let op = "-"; - let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2)); + let hash = calc_fn_hash(empty(), op, 2, empty()); let mut args = StaticVec::new(); args.push(expr); @@ -1388,7 +1389,7 @@ fn parse_unary<'a>( args.push(parse_primary(input, state, level + 1, allow_stmt_expr)?); let op = "!"; - let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2)); + let hash = calc_fn_hash(empty(), op, 2, empty()); Ok(Expr::FnCall(Box::new(( (op.into(), pos), @@ -1492,7 +1493,7 @@ fn parse_op_assignment_stmt<'a>( args.push(lhs_copy); args.push(rhs); - let hash = calc_fn_hash(empty(), &op, repeat(EMPTY_TYPE_ID()).take(args.len())); + let hash = calc_fn_hash(empty(), &op, args.len(), empty()); let rhs_expr = Expr::FnCall(Box::new(((op, pos), None, hash, args, None))); make_assignment_stmt(state, lhs, rhs_expr, pos) @@ -1780,7 +1781,7 @@ fn parse_binary_op<'a>( let cmp_def = Some(false.into()); let op = op_token.syntax(); - let hash = calc_fn_hash(empty(), &op, repeat(EMPTY_TYPE_ID()).take(2)); + let hash = calc_fn_hash(empty(), &op, 2, empty()); let mut args = StaticVec::new(); args.push(root); @@ -1839,8 +1840,7 @@ fn parse_binary_op<'a>( Expr::FnCall(x) => { let ((id, _), _, hash, args, _) = x.as_mut(); // Recalculate function call hash because there is an additional argument - let args_iter = repeat(EMPTY_TYPE_ID()).take(args.len() + 1); - *hash = calc_fn_hash(empty(), id, args_iter); + *hash = calc_fn_hash(empty(), id, args.len() + 1, empty()); } _ => (), } @@ -2540,12 +2540,8 @@ fn parse_global_level<'a>( let mut state = ParseState::new(max_expr_depth.1); let func = parse_fn(input, &mut state, access, 0, true)?; - // Qualifiers (none) + function name + argument `TypeId`'s - let hash = calc_fn_hash( - empty(), - &func.name, - repeat(EMPTY_TYPE_ID()).take(func.params.len()), - ); + // Qualifiers (none) + function name + number of arguments. + let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty()); functions.insert(hash, func); continue; diff --git a/src/utils.rs b/src/utils.rs index a8f73390..126f6809 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -21,11 +21,6 @@ use crate::stdlib::collections::hash_map::DefaultHasher; #[cfg(feature = "no_std")] use ahash::AHasher; -#[inline(always)] -pub fn EMPTY_TYPE_ID() -> TypeId { - TypeId::of::<()>() -} - /// Calculate a `u64` hash key from a module-qualified function name and parameter types. /// /// Module names are passed in via `&str` references from an iterator. @@ -37,6 +32,7 @@ pub fn EMPTY_TYPE_ID() -> TypeId { pub fn calc_fn_spec<'a>( modules: impl Iterator, fn_name: &str, + num: usize, params: impl Iterator, ) -> u64 { #[cfg(feature = "no_std")] @@ -47,6 +43,7 @@ pub fn calc_fn_spec<'a>( // We always skip the first module modules.skip(1).for_each(|m| m.hash(&mut s)); s.write(fn_name.as_bytes()); + s.write_usize(num); params.for_each(|t| t.hash(&mut s)); s.finish() }