diff --git a/src/ast.rs b/src/ast.rs index 688bb3b2..33b784d0 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,6 +1,7 @@ //! Module defining the AST (abstract syntax tree). use crate::calc_fn_hash; +use crate::func::hashing::DEFAULT_HASH; use crate::module::NamespaceRef; use crate::tokenizer::Token; use crate::types::dynamic::Union; @@ -1801,7 +1802,7 @@ impl OpAssignment<'_> { pub struct FnCallHashes { /// Pre-calculated hash for a script-defined function ([`None`] if native functions only). #[cfg(not(feature = "no_function"))] - pub script: Option, + pub script: u64, /// Pre-calculated hash for a native Rust function with no parameter types. pub native: u64, } @@ -1809,11 +1810,11 @@ pub struct FnCallHashes { impl fmt::Debug for FnCallHashes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "no_function"))] - if let Some(script) = self.script { - return if script == self.native { + if self.script != 0 { + return if self.script == self.native { fmt::Debug::fmt(&self.native, f) } else { - write!(f, "({}, {})", script, self.native) + write!(f, "({}, {})", self.script, self.native) }; } @@ -1824,9 +1825,11 @@ impl fmt::Debug for FnCallHashes { impl From for FnCallHashes { #[inline(always)] fn from(hash: u64) -> Self { + let hash = if hash == 0 { DEFAULT_HASH } else { hash }; + Self { #[cfg(not(feature = "no_function"))] - script: Some(hash), + script: hash, native: hash, } } @@ -1839,8 +1842,8 @@ impl FnCallHashes { pub const fn from_native(hash: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: None, - native: hash, + script: 0, + native: if hash == 0 { DEFAULT_HASH } else { hash }, } } /// Create a [`FnCallHashes`] with both native Rust and script function hashes. @@ -1849,8 +1852,8 @@ impl FnCallHashes { pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: Some(script), - native, + script: if script == 0 { DEFAULT_HASH } else { script }, + native: if native == 0 { DEFAULT_HASH } else { native }, } } /// Is this [`FnCallHashes`] native Rust only? @@ -1858,7 +1861,7 @@ impl FnCallHashes { #[must_use] pub const fn is_native_only(&self) -> bool { #[cfg(not(feature = "no_function"))] - return self.script.is_none(); + return self.script == 0; #[cfg(feature = "no_function")] return true; diff --git a/src/func/call.rs b/src/func/call.rs index aab599c2..4f0e1018 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -177,6 +177,10 @@ impl Engine { allow_dynamic: bool, is_op_assignment: bool, ) -> Option<&'s FnResolutionCacheEntry> { + if hash_script == 0 { + return None; + } + let fn_name = fn_name.as_ref(); let mut hash = args.as_ref().map_or(hash_script, |args| { @@ -562,13 +566,10 @@ impl Engine { // Scripted function call? #[cfg(not(feature = "no_function"))] - let hash_script = hashes.script; - - #[cfg(not(feature = "no_function"))] - if let Some(FnResolutionCacheEntry { func, source }) = hash_script.and_then(|hash| { - self.resolve_fn(mods, state, lib, fn_name, hash, None, false, false) - .cloned() - }) { + if let Some(FnResolutionCacheEntry { func, source }) = self + .resolve_fn(mods, state, lib, fn_name, hashes.script, None, false, false) + .cloned() + { // Script function call assert!(func.is_script()); diff --git a/src/func/hashing.rs b/src/func/hashing.rs index d0ede330..0159e84e 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -8,7 +8,13 @@ use std::{ iter::empty, }; -/// A hasher that only takes one single [`u64`] and returns it as a hash key. +pub const DEFAULT_HASH: u64 = 42; + +/// A hasher that only takes one single [`u64`] and returns it as a non-zero hash key. +/// +/// # Zeros +/// +/// If the value is zero, then it is mapped to `DEFAULT_HASH`. /// /// # Panics /// @@ -29,6 +35,10 @@ impl Hasher for StraightHasher { key.copy_from_slice(bytes); self.0 = u64::from_ne_bytes(key); + + if self.0 == 0 { + self.0 = DEFAULT_HASH + } } } @@ -41,7 +51,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] fn build_hasher(&self) -> Self::Hasher { - StraightHasher(42) + StraightHasher(DEFAULT_HASH) } } @@ -52,11 +62,15 @@ pub fn get_hasher() -> ahash::AHasher { ahash::AHasher::default() } -/// Calculate a [`u64`] hash key from a namespace-qualified variable name. +/// Calculate a non-zero [`u64`] hash key from a namespace-qualified variable name. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator. /// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. +/// /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. @@ -76,15 +90,23 @@ pub fn calc_qualified_var_hash<'a>( .for_each(|m| m.as_ref().hash(s)); len.hash(s); var_name.as_ref().hash(s); - s.finish() + + match s.finish() { + 0 => DEFAULT_HASH, + r => r, + } } -/// Calculate a [`u64`] hash key from a namespace-qualified function name +/// Calculate a non-zero [`u64`] hash key from a namespace-qualified function name /// and the number of parameters, but no parameter types. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator. /// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. +/// /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. @@ -106,22 +128,34 @@ pub fn calc_qualified_fn_hash( len.hash(s); fn_name.as_ref().hash(s); num.hash(s); - s.finish() + + match s.finish() { + 0 => DEFAULT_HASH, + r => r, + } } -/// Calculate a [`u64`] hash key from a non-namespace-qualified function name +/// Calculate a non-zero [`u64`] hash key from a non-namespace-qualified function name /// and the number of parameters, but no parameter types. /// /// Parameter types are passed in via [`TypeId`] values from an iterator. +/// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. #[inline(always)] #[must_use] pub fn calc_fn_hash(fn_name: impl AsRef, num: usize) -> u64 { calc_qualified_fn_hash(empty::<&str>(), fn_name, num) } -/// Calculate a [`u64`] hash key from a list of parameter types. +/// Calculate a non-zero [`u64`] hash key from a list of parameter types. /// /// Parameter types are passed in via [`TypeId`] values from an iterator. +/// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. #[inline] #[must_use] pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { @@ -129,12 +163,23 @@ pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { let mut len = 0; params.inspect(|_| len += 1).for_each(|t| t.hash(s)); len.hash(s); - s.finish() + + match s.finish() { + 0 => DEFAULT_HASH, + r => r, + } } /// Combine two [`u64`] hashes by taking the XOR of them. +/// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. #[inline(always)] #[must_use] pub const fn combine_hashes(a: u64, b: u64) -> u64 { - a ^ b + match a ^ b { + 0 => DEFAULT_HASH, + r => r, + } }