diff --git a/src/ast.rs b/src/ast.rs index bc801376..f2e41d98 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -9,7 +9,7 @@ use crate::stdlib::{ collections::HashMap, fmt, hash::Hash, - num::NonZeroUsize, + num::{NonZeroU64, NonZeroUsize}, ops::{Add, AddAssign}, string::String, vec, @@ -914,10 +914,8 @@ pub struct BinaryExpr { #[derive(Debug, Clone, Default)] pub struct FnCallExpr { /// Pre-calculated hash for a script-defined function of the same name and number of parameters. - pub hash: u64, - /// Call native functions only? Set to [`true`] to skip searching for script-defined function overrides - /// when it is certain that the function must be native (e.g. an operator). - pub native_only: bool, + /// None if native Rust only. + pub hash_script: Option, /// Does this function call capture the parent scope? pub capture: bool, /// Default value when the function is not found, mostly used to provide a default for comparison functions. @@ -964,7 +962,14 @@ pub enum Expr { /// () Unit(Position), /// Variable access - (optional index, optional modules, hash, variable name) - Variable(Box<(Option, Option>, u64, Ident)>), + Variable( + Box<( + Option, + Option>, + Option, + Ident, + )>, + ), /// Property access - (getter, setter), prop Property(Box<((ImmutableString, ImmutableString), Ident)>), /// { [statement][Stmt] } diff --git a/src/engine.rs b/src/engine.rs index bb6cb872..fdc735ea 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -19,6 +19,7 @@ use crate::stdlib::{ fmt, format, hash::{Hash, Hasher}, iter::{empty, once}, + num::NonZeroU64, num::NonZeroUsize, ops::DerefMut, string::{String, ToString}, @@ -112,23 +113,15 @@ impl Imports { } /// Does the specified function hash key exist in this stack of imported [modules][Module]? #[allow(dead_code)] - pub fn contains_fn(&self, hash: u64) -> bool { - if hash == 0 { - false - } else { - self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash)) - } + pub fn contains_fn(&self, hash: NonZeroU64) -> bool { + self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash)) } /// Get specified function via its hash key. - pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> { - if hash == 0 { - None - } else { - self.0 - .iter() - .rev() - .find_map(|(_, m)| m.get_qualified_fn(hash)) - } + pub fn get_fn(&self, hash: NonZeroU64) -> Option<&CallableFunction> { + self.0 + .iter() + .rev() + .find_map(|(_, m)| m.get_qualified_fn(hash)) } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of imported [modules][Module]? #[allow(dead_code)] @@ -478,7 +471,7 @@ pub struct State { /// Number of modules loaded. pub modules: usize, /// Cached lookup values for function hashes. - pub functions_cache: HashMap, StraightHasherBuilder>, + pub functions_cache: HashMap, StraightHasherBuilder>, } impl State { @@ -870,15 +863,18 @@ impl Engine { // Qualified variable (_, Some(modules), hash_var, Ident { name, pos }) => { let module = search_imports(mods, state, modules)?; - let target = module.get_qualified_var(*hash_var).map_err(|mut err| { - match *err { - EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { - *err_name = format!("{}{}", modules, name); - } - _ => (), - } - err.fill_position(*pos) - })?; + let target = + module + .get_qualified_var(hash_var.unwrap()) + .map_err(|mut err| { + match *err { + EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { + *err_name = format!("{}{}", modules, name); + } + _ => (), + } + err.fill_position(*pos) + })?; // Module variables are constant let mut target = target.clone(); @@ -1047,7 +1043,7 @@ impl Engine { let args = &mut [target_val, &mut idx_val2, &mut new_val.0]; self.exec_fn_call( - mods, state, lib, FN_IDX_SET, 0, args, is_ref, true, false, + mods, state, lib, FN_IDX_SET, None, args, is_ref, true, false, new_val.1, None, None, level, ) .map_err(|err| match *err { @@ -1083,16 +1079,15 @@ impl Engine { Expr::FnCall(x, pos) if x.namespace.is_none() => { let FnCallExpr { name, - native_only: native, - hash, + hash_script: hash, def_value, .. } = x.as_ref(); let def_value = def_value.as_ref(); let args = idx_val.as_fn_call_args(); self.make_method_call( - mods, state, lib, name, *hash, target, args, def_value, *native, false, - *pos, level, + mods, state, lib, name, *hash, target, args, def_value, false, *pos, + level, ) } // xxx.module::fn_name(...) - syntax error @@ -1126,7 +1121,7 @@ impl Engine { let mut new_val = new_val; let mut args = [target_val, &mut new_val.as_mut().unwrap().0]; self.exec_fn_call( - mods, state, lib, setter, 0, &mut args, is_ref, true, false, *pos, + mods, state, lib, setter, None, &mut args, is_ref, true, false, *pos, None, None, level, ) .map(|(v, _)| (v, true)) @@ -1137,7 +1132,7 @@ impl Engine { let ((getter, _), Ident { pos, .. }) = x.as_ref(); let mut args = [target_val]; self.exec_fn_call( - mods, state, lib, getter, 0, &mut args, is_ref, true, false, *pos, + mods, state, lib, getter, None, &mut args, is_ref, true, false, *pos, None, None, level, ) .map(|(v, _)| (v, false)) @@ -1158,16 +1153,15 @@ impl Engine { Expr::FnCall(x, pos) if x.namespace.is_none() => { let FnCallExpr { name, - native_only: native, - hash, + hash_script: hash, def_value, .. } = x.as_ref(); let def_value = def_value.as_ref(); let args = idx_val.as_fn_call_args(); let (val, _) = self.make_method_call( - mods, state, lib, name, *hash, target, args, def_value, - *native, false, *pos, level, + mods, state, lib, name, *hash, target, args, def_value, false, + *pos, level, )?; val.into() } @@ -1193,7 +1187,7 @@ impl Engine { let args = &mut arg_values[..1]; let (mut val, updated) = self .exec_fn_call( - mods, state, lib, getter, 0, args, is_ref, true, false, + mods, state, lib, getter, None, args, is_ref, true, false, *pos, None, None, level, ) .map_err(|err| err.fill_position(*pos))?; @@ -1220,7 +1214,7 @@ impl Engine { // Re-use args because the first &mut parameter will not be consumed arg_values[1] = val; self.exec_fn_call( - mods, state, lib, setter, 0, arg_values, is_ref, true, + mods, state, lib, setter, None, arg_values, is_ref, true, false, *pos, None, None, level, ) .or_else( @@ -1240,16 +1234,15 @@ impl Engine { Expr::FnCall(f, pos) if f.namespace.is_none() => { let FnCallExpr { name, - native_only: native, - hash, + hash_script: hash, def_value, .. } = f.as_ref(); let def_value = def_value.as_ref(); let args = idx_val.as_fn_call_args(); let (mut val, _) = self.make_method_call( - mods, state, lib, name, *hash, target, args, def_value, - *native, false, *pos, level, + mods, state, lib, name, *hash, target, args, def_value, false, + *pos, level, )?; let val = &mut val; let target = &mut val.into(); @@ -1501,8 +1494,8 @@ impl Engine { let mut idx = idx; let args = &mut [target, &mut idx]; self.exec_fn_call( - _mods, state, _lib, FN_IDX_GET, 0, args, _is_ref, true, false, idx_pos, None, - None, _level, + _mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, false, idx_pos, + None, None, _level, ) .map(|(v, _)| v.into()) .map_err(|err| match *err { @@ -1553,7 +1546,8 @@ impl Engine { // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. let hash = - calc_native_fn_hash(empty(), OP_EQUALS, args.iter().map(|a| a.type_id())); + calc_native_fn_hash(empty(), OP_EQUALS, args.iter().map(|a| a.type_id())) + .unwrap(); let pos = rhs.position(); @@ -1680,7 +1674,7 @@ impl Engine { let ((getter, _), Ident { pos, .. }) = p.as_ref(); let mut args = [target.as_mut()]; self.exec_fn_call( - mods, state, lib, getter, 0, &mut args, is_ref, true, false, *pos, + mods, state, lib, getter, None, &mut args, is_ref, true, false, *pos, None, None, level, ) .map(|(v, _)| (v.into(), *pos)) @@ -1778,17 +1772,16 @@ impl Engine { Expr::FnCall(x, pos) if x.namespace.is_none() => { let FnCallExpr { name, - native_only: native, capture: cap_scope, - hash, + hash_script: hash, args, def_value, .. } = x.as_ref(); let def_value = def_value.as_ref(); self.make_function_call( - scope, mods, state, lib, this_ptr, name, args, def_value, *hash, *native, - false, *pos, *cap_scope, level, + scope, mods, state, lib, this_ptr, name, args, def_value, *hash, false, *pos, + *cap_scope, level, ) } @@ -1797,15 +1790,16 @@ impl Engine { let FnCallExpr { name, namespace, - hash, + hash_script: hash, args, def_value, .. } = x.as_ref(); let namespace = namespace.as_ref().map(|v| v.as_ref()); + let hash = hash.unwrap(); let def_value = def_value.as_ref(); self.make_qualified_function_call( - scope, mods, state, lib, this_ptr, namespace, name, args, def_value, *hash, + scope, mods, state, lib, this_ptr, namespace, name, args, def_value, hash, *pos, level, ) } @@ -1966,7 +1960,7 @@ impl Engine { // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. let arg_types = once(lhs_ptr.as_mut().type_id()).chain(once(rhs_val.type_id())); - let hash_fn = calc_native_fn_hash(empty(), op, arg_types); + let hash_fn = calc_native_fn_hash(empty(), op, arg_types).unwrap(); match self .global_namespace @@ -2015,8 +2009,8 @@ impl Engine { // Run function let (value, _) = self.exec_fn_call( - mods, state, lib, op, 0, args, false, false, false, *op_pos, None, - None, level, + mods, state, lib, op, None, args, false, false, false, *op_pos, + None, None, level, )?; let value = value.flatten(); @@ -2051,7 +2045,7 @@ impl Engine { let result = self .exec_fn_call( - mods, state, lib, op, 0, args, false, false, false, *op_pos, None, + mods, state, lib, op, None, args, false, false, false, *op_pos, None, None, level, ) .map(|(v, _)| v)?; diff --git a/src/fn_call.rs b/src/fn_call.rs index 6d19919d..c5eaabc9 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -15,6 +15,7 @@ use crate::stdlib::{ format, iter::{empty, once}, mem, + num::NonZeroU64, ops::Deref, string::ToString, vec::Vec, @@ -161,7 +162,7 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - hash_fn: u64, + hash_fn: NonZeroU64, args: &mut FnCallArgs, is_ref: bool, pub_only: bool, @@ -453,22 +454,22 @@ impl Engine { &self, mods: Option<&Imports>, lib: &[&Module], - hash_fn: u64, - hash_script: u64, + hash_fn: Option, + hash_script: Option, pub_only: bool, ) -> bool { // First check script-defined functions - (hash_script != 0 && lib.iter().any(|&m| m.contains_fn(hash_script, pub_only))) - //|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only)) + hash_script.map(|hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))).unwrap_or(false) + //|| hash_fn.map(|hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))).unwrap_or(false) // Then check registered functions - //|| (hash_script != 0 && self.global_namespace.contains_fn(hash_script, pub_only)) - || self.global_namespace.contains_fn(hash_fn, false) + //|| hash_script.map(|hash| self.global_namespace.contains_fn(hash, pub_only)).unwrap_or(false) + || hash_fn.map(|hash| self.global_namespace.contains_fn(hash, false)).unwrap_or(false) // Then check packages - || (hash_script != 0 && self.global_modules.iter().any(|m| m.contains_fn(hash_script, false))) - || self.global_modules.iter().any(|m| m.contains_fn(hash_fn, false)) + || hash_script.map(|hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))).unwrap_or(false) + || hash_fn.map(|hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))).unwrap_or(false) // Then check imported modules - || (hash_script != 0 && mods.map(|m| m.contains_fn(hash_script)).unwrap_or(false)) - || mods.map(|m| m.contains_fn(hash_fn)).unwrap_or(false) + || hash_script.map(|hash| mods.map(|m| m.contains_fn(hash)).unwrap_or(false)).unwrap_or(false) + || hash_fn.map(|hash| mods.map(|m| m.contains_fn(hash)).unwrap_or(false)).unwrap_or(false) } /// Perform an actual function call, native Rust or scripted, taking care of special functions. @@ -484,7 +485,7 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - hash_script: u64, + hash_script: Option, args: &mut FnCallArgs, is_ref: bool, _is_method: bool, @@ -534,9 +535,11 @@ impl Engine { // Script-like function found #[cfg(not(feature = "no_function"))] - _ if hash_script != 0 - && self.has_override(Some(mods), lib, 0, hash_script, pub_only) => + _ if hash_script.is_some() + && self.has_override(Some(mods), lib, None, hash_script, pub_only) => { + let hash_script = hash_script.unwrap(); + // Get function let (func, mut source) = lib .iter() @@ -636,7 +639,16 @@ impl Engine { // Normal native function call _ => self.call_native_fn( - mods, state, lib, fn_name, hash_fn, args, is_ref, pub_only, pos, def_val, + mods, + state, + lib, + fn_name, + hash_fn.unwrap(), + args, + is_ref, + pub_only, + pos, + def_val, ), } } @@ -723,11 +735,10 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - hash_script: u64, + hash_script: Option, target: &mut crate::engine::Target, mut call_args: StaticVec, def_val: Option<&Dynamic>, - native: bool, pub_only: bool, pos: Position, level: usize, @@ -745,11 +756,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hash - let hash = if native { - 0 - } else { - calc_script_fn_hash(empty(), fn_name, args_len) - }; + let hash = hash_script.and_then(|_| calc_script_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 @@ -773,11 +780,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hash - let hash = if native { - 0 - } else { - calc_script_fn_hash(empty(), fn_name, args_len) - }; + let hash = hash_script.and_then(|_| calc_script_fn_hash(empty(), fn_name, args_len)); // Replace the first argument with the object pointer, adding the curried arguments let mut curry = fn_ptr.curry().iter().cloned().collect::>(); let mut arg_values = once(obj) @@ -837,17 +840,14 @@ impl Engine { .enumerate() .for_each(|(i, v)| call_args.insert(i, v)); // Recalculate the hash based on the new function name and new arguments - hash = if native { - 0 - } else { - calc_script_fn_hash(empty(), fn_name, call_args.len()) - }; + hash = hash_script + .and_then(|_| calc_script_fn_hash(empty(), fn_name, call_args.len())); } } }; - if native { - hash = 0; + if hash_script.is_none() { + hash = None; } // Attached object pointer in front of the arguments @@ -881,8 +881,7 @@ impl Engine { fn_name: &str, args_expr: impl AsRef<[Expr]>, def_val: Option<&Dynamic>, - mut hash_script: u64, - native: bool, + mut hash_script: Option, pub_only: bool, pos: Position, capture_scope: bool, @@ -951,7 +950,7 @@ impl Engine { if name == KEYWORD_FN_PTR_CALL && args_expr.len() >= 1 - && !self.has_override(Some(mods), lib, 0, hash_script, pub_only) + && !self.has_override(Some(mods), lib, None, hash_script, pub_only) { let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; @@ -1071,11 +1070,21 @@ impl Engine { } } - let hash = if native { 0 } else { hash_script }; let args = args.as_mut(); self.exec_fn_call( - mods, state, lib, name, hash, args, is_ref, false, pub_only, pos, capture, def_val, + mods, + state, + lib, + name, + hash_script, + args, + is_ref, + false, + pub_only, + pos, + capture, + def_val, level, ) .map(|(v, _)| v) @@ -1093,7 +1102,7 @@ impl Engine { fn_name: &str, args_expr: impl AsRef<[Expr]>, def_val: Option<&Dynamic>, - hash_script: u64, + hash_script: NonZeroU64, pos: Position, level: usize, ) -> Result> { @@ -1167,9 +1176,10 @@ impl Engine { // 2) Calculate a second hash with no qualifiers, empty function name, // and the actual list of argument `TypeId`'.s let hash_fn_args = - calc_native_fn_hash(empty(), "", args.iter().map(|a| a.type_id())); + calc_native_fn_hash(empty(), "", args.iter().map(|a| a.type_id())).unwrap(); // 3) The final hash is the XOR of the two hashes. - let hash_qualified_fn = hash_script ^ hash_fn_args; + let hash_qualified_fn = + NonZeroU64::new(hash_script.get() ^ hash_fn_args.get()).unwrap(); module.get_qualified_fn(hash_qualified_fn) } diff --git a/src/lib.rs b/src/lib.rs index 00f60db2..9f7a8773 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,18 @@ //! # Rhai - embedded scripting for Rust //! +//! ![Rhai logo](https://schungx.github.io/rhai/images/logo/rhai-banner-transparent-colour.svg) +//! //! Rhai is a tiny, simple and fast embedded scripting language for Rust //! that gives you a safe and easy way to add scripting to your applications. -//! It provides a familiar syntax based on JavaScript and Rust and a simple Rust interface. -//! Here is a quick example. //! -//! First, the contents of `my_script.rhai`: +//! It provides a familiar syntax based on JavaScript+Rust and a simple Rust interface. +//! +//! # A Quick Example +//! +//! ## Contents of `my_script.rhai` //! //! ```,ignore -//! // Brute force factorial function +//! /// Brute force factorial function //! fn factorial(x) { //! if x == 1 { return 1; } //! x * factorial(x - 1) @@ -18,7 +22,7 @@ //! compute(factorial(10)) //! ``` //! -//! And the Rust part: +//! ## The Rust part //! //! ```,no_run //! use rhai::{Engine, EvalAltResult, RegisterFn}; diff --git a/src/module/mod.rs b/src/module/mod.rs index 6ed3d044..54f3986b 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -10,6 +10,7 @@ use crate::stdlib::{ collections::HashMap, fmt, format, iter::empty, + num::NonZeroU64, num::NonZeroUsize, ops::{Add, AddAssign, Deref, DerefMut}, string::{String, ToString}, @@ -137,12 +138,12 @@ pub struct Module { /// Module variables. variables: HashMap, /// Flattened collection of all module variables, including those in sub-modules. - all_variables: HashMap, + all_variables: HashMap, /// External Rust functions. - functions: HashMap, + functions: HashMap, /// Flattened collection of all external Rust functions, native or scripted. /// including those in sub-modules. - all_functions: HashMap, + all_functions: HashMap, /// Iterator functions, keyed by the type producing the iterator. type_iterators: HashMap, /// Flattened collection of iterator functions, including those in sub-modules. @@ -398,16 +399,15 @@ impl Module { /// Get a reference to a namespace-qualified variable. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// - /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. + /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. #[inline(always)] - pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box> { - if hash_var == 0 { - Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()) - } else { - self.all_variables.get(&hash_var).ok_or_else(|| { - EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into() - }) - } + pub(crate) fn get_qualified_var( + &self, + hash_var: NonZeroU64, + ) -> Result<&Dynamic, Box> { + self.all_variables.get(&hash_var).ok_or_else(|| { + EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into() + }) } /// Set a script-defined function into the module. @@ -415,12 +415,12 @@ impl Module { /// If there is an existing function of the same name and number of arguments, it is replaced. #[cfg(not(feature = "no_function"))] #[inline] - pub(crate) fn set_script_fn(&mut self, fn_def: impl Into>) -> u64 { + pub(crate) fn set_script_fn(&mut self, fn_def: impl Into>) -> NonZeroU64 { let fn_def = fn_def.into(); // None + function name + number of arguments. let num_params = fn_def.params.len(); - let hash_script = crate::calc_script_fn_hash(empty(), &fn_def.name, num_params); + let hash_script = crate::calc_script_fn_hash(empty(), &fn_def.name, num_params).unwrap(); let mut param_names: StaticVec<_> = fn_def.params.iter().cloned().collect(); param_names.push("Dynamic".into()); self.functions.insert( @@ -526,7 +526,7 @@ impl Module { /// Does the particular Rust function exist in the module? /// - /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. + /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. /// It is also returned by the `set_fn_XXX` calls. /// /// # Example @@ -539,10 +539,8 @@ impl Module { /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] - pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool { - if hash_fn == 0 { - false - } else if public_only { + pub fn contains_fn(&self, hash_fn: NonZeroU64, public_only: bool) -> bool { + if public_only { self.functions .get(&hash_fn) .map(|FuncInfo { access, .. }| access.is_public()) @@ -554,7 +552,7 @@ impl Module { /// Update the metadata (parameter names/types and return type) of a registered function. /// - /// The [`u64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or + /// The [`NonZeroU64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or /// the function [`crate::calc_script_fn_hash`]. /// /// Each parameter name/type pair should be a single string of the format: `var_name: type`. @@ -563,7 +561,7 @@ impl Module { /// In other words, the number of entries should be one larger than the number of parameters. pub fn update_fn_metadata<'a>( &mut self, - hash_fn: u64, + hash_fn: NonZeroU64, arg_names: impl AsRef<[&'a str]>, ) -> &mut Self { if let Some(f) = self.functions.get_mut(&hash_fn) { @@ -574,9 +572,13 @@ impl Module { /// Update the namespace of a registered function. /// - /// The [`u64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or + /// The [`NonZeroU64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or /// the function [`crate::calc_script_fn_hash`]. - pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { + pub fn update_fn_namespace( + &mut self, + hash_fn: NonZeroU64, + namespace: FnNamespace, + ) -> &mut Self { if let Some(f) = self.functions.get_mut(&hash_fn) { f.namespace = namespace; } @@ -599,10 +601,11 @@ impl Module { arg_names: Option<&[&str]>, arg_types: &[TypeId], func: CallableFunction, - ) -> u64 { + ) -> NonZeroU64 { let name = name.into(); - let hash_fn = crate::calc_native_fn_hash(empty(), &name, arg_types.iter().cloned()); + let hash_fn = + crate::calc_native_fn_hash(empty(), &name, arg_types.iter().cloned()).unwrap(); let param_types = arg_types .into_iter() @@ -712,7 +715,7 @@ impl Module { func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); @@ -748,7 +751,7 @@ impl Module { &mut self, name: impl Into, func: impl Fn() -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from); let arg_types = []; self.set_fn( @@ -783,7 +786,7 @@ impl Module { &mut self, name: impl Into, func: impl Fn(A) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { func(cast_arg::(&mut args[0])).map(Dynamic::from) }; @@ -823,7 +826,7 @@ impl Module { name: impl Into, namespace: FnNamespace, func: impl Fn(&mut A) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { func(&mut args[0].write_lock::().unwrap()).map(Dynamic::from) }; @@ -862,7 +865,7 @@ impl Module { &mut self, name: impl Into, func: impl Fn(&mut A) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { self.set_fn_1_mut( crate::engine::make_getter(&name.into()), FnNamespace::Global, @@ -894,7 +897,7 @@ impl Module { &mut self, name: impl Into, func: impl Fn(A, B) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let a = cast_arg::(&mut args[0]); let b = cast_arg::(&mut args[1]); @@ -941,7 +944,7 @@ impl Module { name: impl Into, namespace: FnNamespace, func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let b = cast_arg::(&mut args[1]); let a = &mut args[0].write_lock::().unwrap(); @@ -987,7 +990,7 @@ impl Module { &mut self, name: impl Into, func: impl Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { self.set_fn_2_mut( crate::engine::make_setter(&name.into()), FnNamespace::Global, @@ -1026,7 +1029,7 @@ impl Module { pub fn set_indexer_get_fn( &mut self, func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } @@ -1073,7 +1076,7 @@ impl Module { &mut self, name: impl Into, func: impl Fn(A, B, C) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let a = cast_arg::(&mut args[0]); let b = cast_arg::(&mut args[1]); @@ -1126,7 +1129,7 @@ impl Module { name: impl Into, namespace: FnNamespace, func: impl Fn(&mut A, B, C) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let b = cast_arg::(&mut args[2]); let c = cast_arg::(&mut args[3]); @@ -1177,7 +1180,7 @@ impl Module { pub fn set_indexer_set_fn( &mut self, func: impl Fn(&mut A, B, C) -> Result<(), Box> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } @@ -1249,7 +1252,7 @@ impl Module { &mut self, getter: impl Fn(&mut A, B) -> Result> + SendSync + 'static, setter: impl Fn(&mut A, B, T) -> Result<(), Box> + SendSync + 'static, - ) -> (u64, u64) { + ) -> (NonZeroU64, NonZeroU64) { ( self.set_indexer_get_fn(getter), self.set_indexer_set_fn(setter), @@ -1286,7 +1289,7 @@ impl Module { &mut self, name: impl Into, func: impl Fn(A, B, C, D) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let a = cast_arg::(&mut args[0]); let b = cast_arg::(&mut args[1]); @@ -1346,7 +1349,7 @@ impl Module { name: impl Into, namespace: FnNamespace, func: impl Fn(&mut A, B, C, D) -> Result> + SendSync + 'static, - ) -> u64 { + ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let b = cast_arg::(&mut args[1]); let c = cast_arg::(&mut args[2]); @@ -1373,48 +1376,43 @@ impl Module { /// Get a Rust function. /// - /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. + /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. /// It is also returned by the `set_fn_XXX` calls. #[inline(always)] - pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> { - if hash_fn == 0 { - None - } else { - self.functions - .get(&hash_fn) - .and_then(|FuncInfo { access, func, .. }| match access { - _ if !public_only => Some(func), - FnAccess::Public => Some(func), - FnAccess::Private => None, - }) - } + pub(crate) fn get_fn( + &self, + hash_fn: NonZeroU64, + public_only: bool, + ) -> Option<&CallableFunction> { + self.functions + .get(&hash_fn) + .and_then(|FuncInfo { access, func, .. }| match access { + _ if !public_only => Some(func), + FnAccess::Public => Some(func), + FnAccess::Private => None, + }) } /// Does the particular namespace-qualified function exist in the module? /// - /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match + /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match /// the hash calculated by [`build_index`][Module::build_index]. - #[inline] - pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool { - if hash_fn == 0 { - false - } else { - self.all_functions.contains_key(&hash_fn) - } + #[inline(always)] + pub fn contains_qualified_fn(&self, hash_fn: NonZeroU64) -> bool { + self.all_functions.contains_key(&hash_fn) } /// Get a namespace-qualified function. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// - /// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match + /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match /// the hash calculated by [`build_index`][Module::build_index]. #[inline(always)] - pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> { - if hash_qualified_fn == 0 { - None - } else { - self.all_functions.get(&hash_qualified_fn) - } + pub(crate) fn get_qualified_fn( + &self, + hash_qualified_fn: NonZeroU64, + ) -> Option<&CallableFunction> { + self.all_functions.get(&hash_qualified_fn) } /// Combine another module into this module. @@ -1760,8 +1758,8 @@ impl Module { fn index_module<'a>( module: &'a Module, qualifiers: &mut Vec<&'a str>, - variables: &mut HashMap, - functions: &mut HashMap, + variables: &mut HashMap, + functions: &mut HashMap, type_iterators: &mut HashMap, ) { module.modules.iter().for_each(|(name, m)| { @@ -1775,7 +1773,7 @@ impl Module { module.variables.iter().for_each(|(var_name, value)| { // Qualifiers + variable name let hash_var = - crate::calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0); + crate::calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0).unwrap(); variables.insert(hash_var, value.clone()); }); @@ -1808,7 +1806,8 @@ impl Module { // Qualifiers + function name + number of arguments. let hash_qualified_script = - crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params); + crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params) + .unwrap(); if !func.is_script() { assert_eq!(*params, param_types.len()); @@ -1822,9 +1821,12 @@ impl Module { empty(), "", param_types.iter().cloned(), - ); + ) + .unwrap(); // 3) The final hash is the XOR of the two hashes. - let hash_qualified_fn = hash_qualified_script ^ hash_fn_args; + let hash_qualified_fn = + NonZeroU64::new(hash_qualified_script.get() ^ hash_fn_args.get()) + .unwrap(); functions.insert(hash_qualified_fn, func.clone()); } else if cfg!(not(feature = "no_function")) { @@ -1912,7 +1914,7 @@ impl Module { /// _(INTERNALS)_ A chain of [module][Module] names to namespace-qualify a variable or function call. /// Exported under the `internals` feature only. /// -/// A [`u64`] hash key is cached for quick search purposes. +/// A [`NonZeroU64`] hash key is cached for quick search purposes. /// /// A [`StaticVec`] is used because most namespace-qualified access contains only one level, /// and it is wasteful to always allocate a [`Vec`] with one element. diff --git a/src/optimize.rs b/src/optimize.rs index 53be762e..ce0d26b9 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -138,7 +138,7 @@ fn call_fn_with_constant_arguments( &mut Default::default(), state.lib, fn_name, - hash_fn, + hash_fn.unwrap(), arg_values.iter_mut().collect::>().as_mut(), false, true, diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 4b241f55..ad1586ab 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -32,7 +32,7 @@ mod fn_ptr_functions { } else { let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize); ctx.engine() - .has_override(ctx.mods, ctx.lib, 0, hash_script, true) + .has_override(ctx.mods, ctx.lib, None, hash_script, true) } } } diff --git a/src/parser.rs b/src/parser.rs index 9a879083..14aa4fc8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -13,7 +13,7 @@ use crate::stdlib::{ format, hash::{Hash, Hasher}, iter::empty, - num::NonZeroUsize, + num::{NonZeroU64, NonZeroUsize}, string::{String, ToString}, vec, vec::Vec, @@ -34,7 +34,7 @@ use crate::FnAccess; type PERR = ParseErrorType; -type FunctionsLib = HashMap; +type FunctionsLib = HashMap; /// A type that encapsulates the current state of the parser. #[derive(Debug)] @@ -335,7 +335,7 @@ fn parse_fn_call( Token::RightParen => { eat_token(input, Token::RightParen); - let hash_script = if let Some(modules) = namespace.as_mut() { + let mut hash_script = if let Some(modules) = namespace.as_mut() { #[cfg(not(feature = "no_module"))] modules.set_index(state.find_module(&modules[0].name)); @@ -352,13 +352,17 @@ fn parse_fn_call( calc_script_fn_hash(empty(), &id, 0) }; + // script functions can only be valid identifiers + if !is_valid_identifier(id.chars()) { + hash_script = None; + } + return Ok(Expr::FnCall( Box::new(FnCallExpr { name: id.to_string().into(), - native_only: !is_valid_identifier(id.chars()), // script functions can only be valid identifiers capture, namespace, - hash: hash_script, + hash_script, args, ..Default::default() }), @@ -383,7 +387,7 @@ fn parse_fn_call( (Token::RightParen, _) => { eat_token(input, Token::RightParen); - let hash_script = if let Some(modules) = namespace.as_mut() { + let mut hash_script = if let Some(modules) = namespace.as_mut() { #[cfg(not(feature = "no_module"))] modules.set_index(state.find_module(&modules[0].name)); @@ -400,13 +404,17 @@ fn parse_fn_call( calc_script_fn_hash(empty(), &id, args.len()) }; + // script functions can only be valid identifiers + if !is_valid_identifier(id.chars()) { + hash_script = None; + } + return Ok(Expr::FnCall( Box::new(FnCallExpr { name: id.to_string().into(), - native_only: !is_valid_identifier(id.chars()), // script functions can only be valid identifiers capture, namespace, - hash: hash_script, + hash_script, args, ..Default::default() }), @@ -973,7 +981,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((None, None, 0, var_name_def))) + Expr::Variable(Box::new((None, None, None, var_name_def))) } // Namespace qualification #[cfg(not(feature = "no_module"))] @@ -987,7 +995,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((None, None, 0, var_name_def))) + Expr::Variable(Box::new((None, None, None, var_name_def))) } // Normal variable access Token::Identifier(s) => { @@ -996,7 +1004,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((index, None, 0, var_name_def))) + Expr::Variable(Box::new((index, None, None, var_name_def))) } // Function call is allowed to have reserved keyword @@ -1006,7 +1014,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((None, None, 0, var_name_def))) + Expr::Variable(Box::new((None, None, None, var_name_def))) } else { return Err(PERR::Reserved(s).into_err(settings.pos)); } @@ -1022,7 +1030,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((None, None, 0, var_name_def))) + Expr::Variable(Box::new((None, None, None, var_name_def))) } } @@ -1109,7 +1117,7 @@ fn parse_primary( name: state.get_interned_string(id2), pos: pos2, }; - Expr::Variable(Box::new((index, modules, 0, var_name_def))) + Expr::Variable(Box::new((index, modules, None, var_name_def))) } (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => { return Err(PERR::Reserved(id2).into_err(pos2)); @@ -1208,16 +1216,12 @@ fn parse_unary( // Call negative function expr => { let op = "-"; - let hash = calc_script_fn_hash(empty(), op, 1); let mut args = StaticVec::new(); args.push(expr); Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - native_only: true, - namespace: None, - hash, args, ..Default::default() }), @@ -1238,16 +1242,12 @@ fn parse_unary( // Call plus function expr => { let op = "+"; - let hash = calc_script_fn_hash(empty(), op, 1); let mut args = StaticVec::new(); args.push(expr); Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - native_only: true, - namespace: None, - hash, args, ..Default::default() }), @@ -1264,13 +1264,10 @@ fn parse_unary( args.push(expr); let op = "!"; - let hash = calc_script_fn_hash(empty(), op, 1); Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - native_only: true, - hash, args, def_value: Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false ..Default::default() @@ -1310,7 +1307,7 @@ fn parse_unary( }); // Qualifiers (none) + function name + number of arguments. - let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()); + let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap(); lib.insert(hash, func); @@ -1723,11 +1720,9 @@ fn parse_binary_op( let cmp_def = Some(false.into()); let op = op_token.syntax(); - let hash = calc_script_fn_hash(empty(), &op, 2); let op_base = FnCallExpr { name: op, - native_only: true, capture: false, ..Default::default() }; @@ -1747,19 +1742,11 @@ fn parse_binary_op( | Token::PowerOf | Token::Ampersand | Token::Pipe - | Token::XOr => Expr::FnCall( - Box::new(FnCallExpr { - hash, - args, - ..op_base - }), - pos, - ), + | Token::XOr => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos), // '!=' defaults to true when passed invalid operands Token::NotEqualsTo => Expr::FnCall( Box::new(FnCallExpr { - hash, args, def_value: Some(true.into()), ..op_base @@ -1774,7 +1761,6 @@ fn parse_binary_op( | Token::GreaterThan | Token::GreaterThanEqualsTo => Expr::FnCall( Box::new(FnCallExpr { - hash, args, def_value: cmp_def, ..op_base @@ -1821,9 +1807,8 @@ fn parse_binary_op( // Accept non-native functions for custom operators Expr::FnCall( Box::new(FnCallExpr { - hash, + hash_script: calc_script_fn_hash(empty(), &s, 2), args, - native_only: false, ..op_base }), pos, @@ -1892,7 +1877,7 @@ fn parse_custom_syntax( segments.push(name.clone()); tokens.push(state.get_interned_string(MARKER_IDENT)); let var_name_def = Ident { name, pos }; - keywords.push(Expr::Variable(Box::new((None, None, 0, var_name_def)))); + keywords.push(Expr::Variable(Box::new((None, None, None, var_name_def)))); } (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); @@ -2549,7 +2534,7 @@ fn parse_stmt( let func = parse_fn(input, &mut new_state, lib, access, settings, comments)?; // Qualifiers (none) + function name + number of arguments. - let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()); + let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap(); lib.insert(hash, func); @@ -2812,7 +2797,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po #[cfg(not(feature = "no_closure"))] externals.iter().for_each(|x| { - args.push(Expr::Variable(Box::new((None, None, 0, x.clone().into())))); + args.push(Expr::Variable(Box::new(( + None, + None, + None, + x.clone().into(), + )))); }); #[cfg(feature = "no_closure")] @@ -2822,12 +2812,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po let curry_func = crate::engine::KEYWORD_FN_PTR_CURRY; - let hash = calc_script_fn_hash(empty(), curry_func, num_externals + 1); + let hash_script = calc_script_fn_hash(empty(), curry_func, num_externals + 1); let expr = Expr::FnCall( Box::new(FnCallExpr { name: curry_func.into(), - hash, + hash_script, args, ..Default::default() }), diff --git a/src/utils.rs b/src/utils.rs index 8513dd6d..6994bb4d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -9,30 +9,31 @@ use crate::stdlib::{ fmt, hash::{BuildHasher, Hash, Hasher}, iter::{empty, FromIterator}, + num::NonZeroU64, ops::{Add, AddAssign, Deref}, str::FromStr, string::{String, ToString}, }; use crate::Shared; -/// A hasher that only takes one single [`u64`] and returns it as a hash key. +/// A hasher that only takes one single [`NonZeroU64`] and returns it as a hash key. /// /// # Panics /// -/// Panics when hashing any data type other than a [`u64`]. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] -pub struct StraightHasher(u64); +/// Panics when hashing any data type other than a [`NonZeroU64`]. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct StraightHasher(NonZeroU64); impl Hasher for StraightHasher { #[inline(always)] fn finish(&self) -> u64 { - self.0 + self.0.get() } #[inline(always)] fn write(&mut self, bytes: &[u8]) { let mut key = [0_u8; 8]; key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes - self.0 = u64::from_le_bytes(key); + self.0 = NonZeroU64::new(u64::from_le_bytes(key)).unwrap(); } } @@ -45,11 +46,11 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] fn build_hasher(&self) -> Self::Hasher { - Default::default() + StraightHasher(NonZeroU64::new(1).unwrap()) } } -/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types. +/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and parameter types. /// Exported under the `internals` feature only. /// /// Module names are passed in via `&str` references from an iterator. @@ -63,11 +64,11 @@ pub fn calc_native_fn_hash<'a>( modules: impl Iterator, fn_name: &str, params: impl Iterator, -) -> u64 { +) -> Option { calc_fn_hash(modules, fn_name, None, params) } -/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name +/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name /// and the number of parameters, but no parameter types. /// Exported under the `internals` feature only. /// @@ -82,7 +83,7 @@ pub fn calc_script_fn_hash<'a>( modules: impl Iterator, fn_name: &str, num: usize, -) -> u64 { +) -> Option { calc_fn_hash(modules, fn_name, Some(num), empty()) } @@ -96,7 +97,7 @@ pub fn get_hasher() -> impl Hasher { s } -/// Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types. +/// Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and parameter types. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator. @@ -109,7 +110,7 @@ fn calc_fn_hash<'a>( fn_name: &str, num: Option, params: impl Iterator, -) -> u64 { +) -> Option { let s = &mut get_hasher(); // Hash a boolean indicating whether the hash is namespace-qualified. @@ -122,7 +123,7 @@ fn calc_fn_hash<'a>( } else { params.for_each(|t| t.hash(s)); } - s.finish() + NonZeroU64::new(s.finish()) } /// The system immutable string type.