Use NonZeroU64 for function hashes.

This commit is contained in:
Stephen Chung 2020-12-24 16:32:43 +08:00
parent c81a59435b
commit 8506640073
9 changed files with 251 additions and 245 deletions

View File

@ -9,7 +9,7 @@ use crate::stdlib::{
collections::HashMap, collections::HashMap,
fmt, fmt,
hash::Hash, hash::Hash,
num::NonZeroUsize, num::{NonZeroU64, NonZeroUsize},
ops::{Add, AddAssign}, ops::{Add, AddAssign},
string::String, string::String,
vec, vec,
@ -914,10 +914,8 @@ pub struct BinaryExpr {
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct FnCallExpr { pub struct FnCallExpr {
/// Pre-calculated hash for a script-defined function of the same name and number of parameters. /// Pre-calculated hash for a script-defined function of the same name and number of parameters.
pub hash: u64, /// None if native Rust only.
/// Call native functions only? Set to [`true`] to skip searching for script-defined function overrides pub hash_script: Option<NonZeroU64>,
/// when it is certain that the function must be native (e.g. an operator).
pub native_only: bool,
/// Does this function call capture the parent scope? /// Does this function call capture the parent scope?
pub capture: bool, pub capture: bool,
/// Default value when the function is not found, mostly used to provide a default for comparison functions. /// 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), Unit(Position),
/// Variable access - (optional index, optional modules, hash, variable name) /// Variable access - (optional index, optional modules, hash, variable name)
Variable(Box<(Option<NonZeroUsize>, Option<Box<NamespaceRef>>, u64, Ident)>), Variable(
Box<(
Option<NonZeroUsize>,
Option<Box<NamespaceRef>>,
Option<NonZeroU64>,
Ident,
)>,
),
/// Property access - (getter, setter), prop /// Property access - (getter, setter), prop
Property(Box<((ImmutableString, ImmutableString), Ident)>), Property(Box<((ImmutableString, ImmutableString), Ident)>),
/// { [statement][Stmt] } /// { [statement][Stmt] }

View File

@ -19,6 +19,7 @@ use crate::stdlib::{
fmt, format, fmt, format,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
iter::{empty, once}, iter::{empty, once},
num::NonZeroU64,
num::NonZeroUsize, num::NonZeroUsize,
ops::DerefMut, ops::DerefMut,
string::{String, ToString}, string::{String, ToString},
@ -112,23 +113,15 @@ impl Imports {
} }
/// Does the specified function hash key exist in this stack of imported [modules][Module]? /// Does the specified function hash key exist in this stack of imported [modules][Module]?
#[allow(dead_code)] #[allow(dead_code)]
pub fn contains_fn(&self, hash: u64) -> bool { pub fn contains_fn(&self, hash: NonZeroU64) -> bool {
if hash == 0 { self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash))
false
} else {
self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash))
}
} }
/// Get specified function via its hash key. /// Get specified function via its hash key.
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> { pub fn get_fn(&self, hash: NonZeroU64) -> Option<&CallableFunction> {
if hash == 0 { self.0
None .iter()
} else { .rev()
self.0 .find_map(|(_, m)| m.get_qualified_fn(hash))
.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]? /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of imported [modules][Module]?
#[allow(dead_code)] #[allow(dead_code)]
@ -478,7 +471,7 @@ pub struct State {
/// Number of modules loaded. /// Number of modules loaded.
pub modules: usize, pub modules: usize,
/// Cached lookup values for function hashes. /// Cached lookup values for function hashes.
pub functions_cache: HashMap<u64, Option<CallableFunction>, StraightHasherBuilder>, pub functions_cache: HashMap<NonZeroU64, Option<CallableFunction>, StraightHasherBuilder>,
} }
impl State { impl State {
@ -870,15 +863,18 @@ impl Engine {
// Qualified variable // Qualified variable
(_, Some(modules), hash_var, Ident { name, pos }) => { (_, Some(modules), hash_var, Ident { name, pos }) => {
let module = search_imports(mods, state, modules)?; let module = search_imports(mods, state, modules)?;
let target = module.get_qualified_var(*hash_var).map_err(|mut err| { let target =
match *err { module
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { .get_qualified_var(hash_var.unwrap())
*err_name = format!("{}{}", modules, name); .map_err(|mut err| {
} match *err {
_ => (), EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
} *err_name = format!("{}{}", modules, name);
err.fill_position(*pos) }
})?; _ => (),
}
err.fill_position(*pos)
})?;
// Module variables are constant // Module variables are constant
let mut target = target.clone(); let mut target = target.clone();
@ -1047,7 +1043,7 @@ impl Engine {
let args = &mut [target_val, &mut idx_val2, &mut new_val.0]; let args = &mut [target_val, &mut idx_val2, &mut new_val.0];
self.exec_fn_call( 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, new_val.1, None, None, level,
) )
.map_err(|err| match *err { .map_err(|err| match *err {
@ -1083,16 +1079,15 @@ impl Engine {
Expr::FnCall(x, pos) if x.namespace.is_none() => { Expr::FnCall(x, pos) if x.namespace.is_none() => {
let FnCallExpr { let FnCallExpr {
name, name,
native_only: native, hash_script: hash,
hash,
def_value, def_value,
.. ..
} = x.as_ref(); } = x.as_ref();
let def_value = def_value.as_ref(); let def_value = def_value.as_ref();
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
self.make_method_call( self.make_method_call(
mods, state, lib, name, *hash, target, args, def_value, *native, false, mods, state, lib, name, *hash, target, args, def_value, false, *pos,
*pos, level, level,
) )
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
@ -1126,7 +1121,7 @@ impl Engine {
let mut new_val = new_val; let mut new_val = new_val;
let mut args = [target_val, &mut new_val.as_mut().unwrap().0]; let mut args = [target_val, &mut new_val.as_mut().unwrap().0];
self.exec_fn_call( 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, None, None, level,
) )
.map(|(v, _)| (v, true)) .map(|(v, _)| (v, true))
@ -1137,7 +1132,7 @@ impl Engine {
let ((getter, _), Ident { pos, .. }) = x.as_ref(); let ((getter, _), Ident { pos, .. }) = x.as_ref();
let mut args = [target_val]; let mut args = [target_val];
self.exec_fn_call( 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, None, None, level,
) )
.map(|(v, _)| (v, false)) .map(|(v, _)| (v, false))
@ -1158,16 +1153,15 @@ impl Engine {
Expr::FnCall(x, pos) if x.namespace.is_none() => { Expr::FnCall(x, pos) if x.namespace.is_none() => {
let FnCallExpr { let FnCallExpr {
name, name,
native_only: native, hash_script: hash,
hash,
def_value, def_value,
.. ..
} = x.as_ref(); } = x.as_ref();
let def_value = def_value.as_ref(); let def_value = def_value.as_ref();
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
let (val, _) = self.make_method_call( let (val, _) = self.make_method_call(
mods, state, lib, name, *hash, target, args, def_value, mods, state, lib, name, *hash, target, args, def_value, false,
*native, false, *pos, level, *pos, level,
)?; )?;
val.into() val.into()
} }
@ -1193,7 +1187,7 @@ impl Engine {
let args = &mut arg_values[..1]; let args = &mut arg_values[..1];
let (mut val, updated) = self let (mut val, updated) = self
.exec_fn_call( .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, *pos, None, None, level,
) )
.map_err(|err| err.fill_position(*pos))?; .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 // Re-use args because the first &mut parameter will not be consumed
arg_values[1] = val; arg_values[1] = val;
self.exec_fn_call( 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, false, *pos, None, None, level,
) )
.or_else( .or_else(
@ -1240,16 +1234,15 @@ impl Engine {
Expr::FnCall(f, pos) if f.namespace.is_none() => { Expr::FnCall(f, pos) if f.namespace.is_none() => {
let FnCallExpr { let FnCallExpr {
name, name,
native_only: native, hash_script: hash,
hash,
def_value, def_value,
.. ..
} = f.as_ref(); } = f.as_ref();
let def_value = def_value.as_ref(); let def_value = def_value.as_ref();
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
let (mut val, _) = self.make_method_call( let (mut val, _) = self.make_method_call(
mods, state, lib, name, *hash, target, args, def_value, mods, state, lib, name, *hash, target, args, def_value, false,
*native, false, *pos, level, *pos, level,
)?; )?;
let val = &mut val; let val = &mut val;
let target = &mut val.into(); let target = &mut val.into();
@ -1501,8 +1494,8 @@ impl Engine {
let mut idx = idx; let mut idx = idx;
let args = &mut [target, &mut idx]; let args = &mut [target, &mut idx];
self.exec_fn_call( self.exec_fn_call(
_mods, state, _lib, FN_IDX_GET, 0, args, _is_ref, true, false, idx_pos, None, _mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, false, idx_pos,
None, _level, None, None, _level,
) )
.map(|(v, _)| v.into()) .map(|(v, _)| v.into())
.map_err(|err| match *err { .map_err(|err| match *err {
@ -1553,7 +1546,8 @@ impl Engine {
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
let hash = 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(); let pos = rhs.position();
@ -1680,7 +1674,7 @@ impl Engine {
let ((getter, _), Ident { pos, .. }) = p.as_ref(); let ((getter, _), Ident { pos, .. }) = p.as_ref();
let mut args = [target.as_mut()]; let mut args = [target.as_mut()];
self.exec_fn_call( 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, None, None, level,
) )
.map(|(v, _)| (v.into(), *pos)) .map(|(v, _)| (v.into(), *pos))
@ -1778,17 +1772,16 @@ impl Engine {
Expr::FnCall(x, pos) if x.namespace.is_none() => { Expr::FnCall(x, pos) if x.namespace.is_none() => {
let FnCallExpr { let FnCallExpr {
name, name,
native_only: native,
capture: cap_scope, capture: cap_scope,
hash, hash_script: hash,
args, args,
def_value, def_value,
.. ..
} = x.as_ref(); } = x.as_ref();
let def_value = def_value.as_ref(); let def_value = def_value.as_ref();
self.make_function_call( self.make_function_call(
scope, mods, state, lib, this_ptr, name, args, def_value, *hash, *native, scope, mods, state, lib, this_ptr, name, args, def_value, *hash, false, *pos,
false, *pos, *cap_scope, level, *cap_scope, level,
) )
} }
@ -1797,15 +1790,16 @@ impl Engine {
let FnCallExpr { let FnCallExpr {
name, name,
namespace, namespace,
hash, hash_script: hash,
args, args,
def_value, def_value,
.. ..
} = x.as_ref(); } = x.as_ref();
let namespace = namespace.as_ref().map(|v| v.as_ref()); let namespace = namespace.as_ref().map(|v| v.as_ref());
let hash = hash.unwrap();
let def_value = def_value.as_ref(); let def_value = def_value.as_ref();
self.make_qualified_function_call( 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, *pos, level,
) )
} }
@ -1966,7 +1960,7 @@ impl Engine {
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. // 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 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 match self
.global_namespace .global_namespace
@ -2015,8 +2009,8 @@ impl Engine {
// Run function // Run function
let (value, _) = self.exec_fn_call( let (value, _) = 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, level, None, None, level,
)?; )?;
let value = value.flatten(); let value = value.flatten();
@ -2051,7 +2045,7 @@ impl Engine {
let result = self let result = self
.exec_fn_call( .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, None, level,
) )
.map(|(v, _)| v)?; .map(|(v, _)| v)?;

View File

@ -15,6 +15,7 @@ use crate::stdlib::{
format, format,
iter::{empty, once}, iter::{empty, once},
mem, mem,
num::NonZeroU64,
ops::Deref, ops::Deref,
string::ToString, string::ToString,
vec::Vec, vec::Vec,
@ -161,7 +162,7 @@ impl Engine {
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
fn_name: &str, fn_name: &str,
hash_fn: u64, hash_fn: NonZeroU64,
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
pub_only: bool, pub_only: bool,
@ -453,22 +454,22 @@ impl Engine {
&self, &self,
mods: Option<&Imports>, mods: Option<&Imports>,
lib: &[&Module], lib: &[&Module],
hash_fn: u64, hash_fn: Option<NonZeroU64>,
hash_script: u64, hash_script: Option<NonZeroU64>,
pub_only: bool, pub_only: bool,
) -> bool { ) -> bool {
// First check script-defined functions // First check script-defined functions
(hash_script != 0 && lib.iter().any(|&m| m.contains_fn(hash_script, pub_only))) hash_script.map(|hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))).unwrap_or(false)
//|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only)) //|| hash_fn.map(|hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))).unwrap_or(false)
// Then check registered functions // Then check registered functions
//|| (hash_script != 0 && self.global_namespace.contains_fn(hash_script, pub_only)) //|| hash_script.map(|hash| self.global_namespace.contains_fn(hash, pub_only)).unwrap_or(false)
|| self.global_namespace.contains_fn(hash_fn, false) || hash_fn.map(|hash| self.global_namespace.contains_fn(hash, false)).unwrap_or(false)
// Then check packages // Then check packages
|| (hash_script != 0 && self.global_modules.iter().any(|m| m.contains_fn(hash_script, false))) || hash_script.map(|hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))).unwrap_or(false)
|| self.global_modules.iter().any(|m| m.contains_fn(hash_fn, false)) || hash_fn.map(|hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))).unwrap_or(false)
// Then check imported modules // Then check imported modules
|| (hash_script != 0 && mods.map(|m| m.contains_fn(hash_script)).unwrap_or(false)) || hash_script.map(|hash| mods.map(|m| m.contains_fn(hash)).unwrap_or(false)).unwrap_or(false)
|| mods.map(|m| m.contains_fn(hash_fn)).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. /// Perform an actual function call, native Rust or scripted, taking care of special functions.
@ -484,7 +485,7 @@ impl Engine {
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
fn_name: &str, fn_name: &str,
hash_script: u64, hash_script: Option<NonZeroU64>,
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
_is_method: bool, _is_method: bool,
@ -534,9 +535,11 @@ impl Engine {
// Script-like function found // Script-like function found
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
_ if hash_script != 0 _ if hash_script.is_some()
&& self.has_override(Some(mods), lib, 0, hash_script, pub_only) => && self.has_override(Some(mods), lib, None, hash_script, pub_only) =>
{ {
let hash_script = hash_script.unwrap();
// Get function // Get function
let (func, mut source) = lib let (func, mut source) = lib
.iter() .iter()
@ -636,7 +639,16 @@ impl Engine {
// Normal native function call // Normal native function call
_ => self.call_native_fn( _ => 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, state: &mut State,
lib: &[&Module], lib: &[&Module],
fn_name: &str, fn_name: &str,
hash_script: u64, hash_script: Option<NonZeroU64>,
target: &mut crate::engine::Target, target: &mut crate::engine::Target,
mut call_args: StaticVec<Dynamic>, mut call_args: StaticVec<Dynamic>,
def_val: Option<&Dynamic>, def_val: Option<&Dynamic>,
native: bool,
pub_only: bool, pub_only: bool,
pos: Position, pos: Position,
level: usize, level: usize,
@ -745,11 +756,7 @@ impl Engine {
let fn_name = fn_ptr.fn_name(); let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len(); let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hash // Recalculate hash
let hash = if native { let hash = hash_script.and_then(|_| calc_script_fn_hash(empty(), fn_name, args_len));
0
} else {
calc_script_fn_hash(empty(), fn_name, args_len)
};
// Arguments are passed as-is, adding the curried arguments // Arguments are passed as-is, adding the curried arguments
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>(); let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
let mut arg_values = curry let mut arg_values = curry
@ -773,11 +780,7 @@ impl Engine {
let fn_name = fn_ptr.fn_name(); let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len(); let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hash // Recalculate hash
let hash = if native { let hash = hash_script.and_then(|_| calc_script_fn_hash(empty(), fn_name, args_len));
0
} else {
calc_script_fn_hash(empty(), fn_name, args_len)
};
// Replace the first argument with the object pointer, adding the curried arguments // Replace the first argument with the object pointer, adding the curried arguments
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>(); let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
let mut arg_values = once(obj) let mut arg_values = once(obj)
@ -837,17 +840,14 @@ impl Engine {
.enumerate() .enumerate()
.for_each(|(i, v)| call_args.insert(i, v)); .for_each(|(i, v)| call_args.insert(i, v));
// Recalculate the hash based on the new function name and new arguments // Recalculate the hash based on the new function name and new arguments
hash = if native { hash = hash_script
0 .and_then(|_| calc_script_fn_hash(empty(), fn_name, call_args.len()));
} else {
calc_script_fn_hash(empty(), fn_name, call_args.len())
};
} }
} }
}; };
if native { if hash_script.is_none() {
hash = 0; hash = None;
} }
// Attached object pointer in front of the arguments // Attached object pointer in front of the arguments
@ -881,8 +881,7 @@ impl Engine {
fn_name: &str, fn_name: &str,
args_expr: impl AsRef<[Expr]>, args_expr: impl AsRef<[Expr]>,
def_val: Option<&Dynamic>, def_val: Option<&Dynamic>,
mut hash_script: u64, mut hash_script: Option<NonZeroU64>,
native: bool,
pub_only: bool, pub_only: bool,
pos: Position, pos: Position,
capture_scope: bool, capture_scope: bool,
@ -951,7 +950,7 @@ impl Engine {
if name == KEYWORD_FN_PTR_CALL if name == KEYWORD_FN_PTR_CALL
&& args_expr.len() >= 1 && 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)?; 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(); let args = args.as_mut();
self.exec_fn_call( 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, level,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
@ -1093,7 +1102,7 @@ impl Engine {
fn_name: &str, fn_name: &str,
args_expr: impl AsRef<[Expr]>, args_expr: impl AsRef<[Expr]>,
def_val: Option<&Dynamic>, def_val: Option<&Dynamic>,
hash_script: u64, hash_script: NonZeroU64,
pos: Position, pos: Position,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -1167,9 +1176,10 @@ impl Engine {
// 2) Calculate a second hash with no qualifiers, empty function name, // 2) Calculate a second hash with no qualifiers, empty function name,
// and the actual list of argument `TypeId`'.s // and the actual list of argument `TypeId`'.s
let hash_fn_args = 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. // 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) module.get_qualified_fn(hash_qualified_fn)
} }

View File

@ -1,14 +1,18 @@
//! # Rhai - embedded scripting for Rust //! # 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 //! 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. //! 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 //! ```,ignore
//! // Brute force factorial function //! /// Brute force factorial function
//! fn factorial(x) { //! fn factorial(x) {
//! if x == 1 { return 1; } //! if x == 1 { return 1; }
//! x * factorial(x - 1) //! x * factorial(x - 1)
@ -18,7 +22,7 @@
//! compute(factorial(10)) //! compute(factorial(10))
//! ``` //! ```
//! //!
//! And the Rust part: //! ## The Rust part
//! //!
//! ```,no_run //! ```,no_run
//! use rhai::{Engine, EvalAltResult, RegisterFn}; //! use rhai::{Engine, EvalAltResult, RegisterFn};

View File

@ -10,6 +10,7 @@ use crate::stdlib::{
collections::HashMap, collections::HashMap,
fmt, format, fmt, format,
iter::empty, iter::empty,
num::NonZeroU64,
num::NonZeroUsize, num::NonZeroUsize,
ops::{Add, AddAssign, Deref, DerefMut}, ops::{Add, AddAssign, Deref, DerefMut},
string::{String, ToString}, string::{String, ToString},
@ -137,12 +138,12 @@ pub struct Module {
/// Module variables. /// Module variables.
variables: HashMap<ImmutableString, Dynamic>, variables: HashMap<ImmutableString, Dynamic>,
/// Flattened collection of all module variables, including those in sub-modules. /// Flattened collection of all module variables, including those in sub-modules.
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>, all_variables: HashMap<NonZeroU64, Dynamic, StraightHasherBuilder>,
/// External Rust functions. /// External Rust functions.
functions: HashMap<u64, FuncInfo, StraightHasherBuilder>, functions: HashMap<NonZeroU64, FuncInfo, StraightHasherBuilder>,
/// Flattened collection of all external Rust functions, native or scripted. /// Flattened collection of all external Rust functions, native or scripted.
/// including those in sub-modules. /// including those in sub-modules.
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>, all_functions: HashMap<NonZeroU64, CallableFunction, StraightHasherBuilder>,
/// Iterator functions, keyed by the type producing the iterator. /// Iterator functions, keyed by the type producing the iterator.
type_iterators: HashMap<TypeId, IteratorFn>, type_iterators: HashMap<TypeId, IteratorFn>,
/// Flattened collection of iterator functions, including those in sub-modules. /// Flattened collection of iterator functions, including those in sub-modules.
@ -398,16 +399,15 @@ impl Module {
/// Get a reference to a namespace-qualified variable. /// Get a reference to a namespace-qualified variable.
/// Name and Position in `EvalAltResult` are None and must be set afterwards. /// 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)] #[inline(always)]
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> { pub(crate) fn get_qualified_var(
if hash_var == 0 { &self,
Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()) hash_var: NonZeroU64,
} else { ) -> Result<&Dynamic, Box<EvalAltResult>> {
self.all_variables.get(&hash_var).ok_or_else(|| { self.all_variables.get(&hash_var).ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into() EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()
}) })
}
} }
/// Set a script-defined function into the module. /// 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. /// If there is an existing function of the same name and number of arguments, it is replaced.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub(crate) fn set_script_fn(&mut self, fn_def: impl Into<Shared<ScriptFnDef>>) -> u64 { pub(crate) fn set_script_fn(&mut self, fn_def: impl Into<Shared<ScriptFnDef>>) -> NonZeroU64 {
let fn_def = fn_def.into(); let fn_def = fn_def.into();
// None + function name + number of arguments. // None + function name + number of arguments.
let num_params = fn_def.params.len(); 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(); let mut param_names: StaticVec<_> = fn_def.params.iter().cloned().collect();
param_names.push("Dynamic".into()); param_names.push("Dynamic".into());
self.functions.insert( self.functions.insert(
@ -526,7 +526,7 @@ impl Module {
/// Does the particular Rust function exist in the 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. /// It is also returned by the `set_fn_XXX` calls.
/// ///
/// # Example /// # Example
@ -539,10 +539,8 @@ impl Module {
/// assert!(module.contains_fn(hash, true)); /// assert!(module.contains_fn(hash, true));
/// ``` /// ```
#[inline] #[inline]
pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool { pub fn contains_fn(&self, hash_fn: NonZeroU64, public_only: bool) -> bool {
if hash_fn == 0 { if public_only {
false
} else if public_only {
self.functions self.functions
.get(&hash_fn) .get(&hash_fn)
.map(|FuncInfo { access, .. }| access.is_public()) .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. /// 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`]. /// the function [`crate::calc_script_fn_hash`].
/// ///
/// Each parameter name/type pair should be a single string of the format: `var_name: type`. /// 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. /// In other words, the number of entries should be one larger than the number of parameters.
pub fn update_fn_metadata<'a>( pub fn update_fn_metadata<'a>(
&mut self, &mut self,
hash_fn: u64, hash_fn: NonZeroU64,
arg_names: impl AsRef<[&'a str]>, arg_names: impl AsRef<[&'a str]>,
) -> &mut Self { ) -> &mut Self {
if let Some(f) = self.functions.get_mut(&hash_fn) { if let Some(f) = self.functions.get_mut(&hash_fn) {
@ -574,9 +572,13 @@ impl Module {
/// Update the namespace of a registered function. /// 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`]. /// 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) { if let Some(f) = self.functions.get_mut(&hash_fn) {
f.namespace = namespace; f.namespace = namespace;
} }
@ -599,10 +601,11 @@ impl Module {
arg_names: Option<&[&str]>, arg_names: Option<&[&str]>,
arg_types: &[TypeId], arg_types: &[TypeId],
func: CallableFunction, func: CallableFunction,
) -> u64 { ) -> NonZeroU64 {
let name = name.into(); 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 let param_types = arg_types
.into_iter() .into_iter()
@ -712,7 +715,7 @@ impl Module {
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>> func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = let f =
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
@ -748,7 +751,7 @@ impl Module {
&mut self, &mut self,
name: impl Into<String>, name: impl Into<String>,
func: impl Fn() -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn() -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from); let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from);
let arg_types = []; let arg_types = [];
self.set_fn( self.set_fn(
@ -783,7 +786,7 @@ impl Module {
&mut self, &mut self,
name: impl Into<String>, name: impl Into<String>,
func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
func(cast_arg::<A>(&mut args[0])).map(Dynamic::from) func(cast_arg::<A>(&mut args[0])).map(Dynamic::from)
}; };
@ -823,7 +826,7 @@ impl Module {
name: impl Into<String>, name: impl Into<String>,
namespace: FnNamespace, namespace: FnNamespace,
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from) func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from)
}; };
@ -862,7 +865,7 @@ impl Module {
&mut self, &mut self,
name: impl Into<String>, name: impl Into<String>,
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
self.set_fn_1_mut( self.set_fn_1_mut(
crate::engine::make_getter(&name.into()), crate::engine::make_getter(&name.into()),
FnNamespace::Global, FnNamespace::Global,
@ -894,7 +897,7 @@ impl Module {
&mut self, &mut self,
name: impl Into<String>, name: impl Into<String>,
func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]); let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]); let b = cast_arg::<B>(&mut args[1]);
@ -941,7 +944,7 @@ impl Module {
name: impl Into<String>, name: impl Into<String>,
namespace: FnNamespace, namespace: FnNamespace,
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[1]); let b = cast_arg::<B>(&mut args[1]);
let a = &mut args[0].write_lock::<A>().unwrap(); let a = &mut args[0].write_lock::<A>().unwrap();
@ -987,7 +990,7 @@ impl Module {
&mut self, &mut self,
name: impl Into<String>, name: impl Into<String>,
func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
self.set_fn_2_mut( self.set_fn_2_mut(
crate::engine::make_setter(&name.into()), crate::engine::make_setter(&name.into()),
FnNamespace::Global, FnNamespace::Global,
@ -1026,7 +1029,7 @@ impl Module {
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>( pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self, &mut self,
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
if TypeId::of::<A>() == TypeId::of::<Array>() { if TypeId::of::<A>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
} }
@ -1073,7 +1076,7 @@ impl Module {
&mut self, &mut self,
name: impl Into<String>, name: impl Into<String>,
func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]); let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]); let b = cast_arg::<B>(&mut args[1]);
@ -1126,7 +1129,7 @@ impl Module {
name: impl Into<String>, name: impl Into<String>,
namespace: FnNamespace, namespace: FnNamespace,
func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[2]); let b = cast_arg::<B>(&mut args[2]);
let c = cast_arg::<C>(&mut args[3]); let c = cast_arg::<C>(&mut args[3]);
@ -1177,7 +1180,7 @@ impl Module {
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>( pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
&mut self, &mut self,
func: impl Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
if TypeId::of::<A>() == TypeId::of::<Array>() { if TypeId::of::<A>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
} }
@ -1249,7 +1252,7 @@ impl Module {
&mut self, &mut self,
getter: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, getter: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
setter: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, setter: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> (u64, u64) { ) -> (NonZeroU64, NonZeroU64) {
( (
self.set_indexer_get_fn(getter), self.set_indexer_get_fn(getter),
self.set_indexer_set_fn(setter), self.set_indexer_set_fn(setter),
@ -1286,7 +1289,7 @@ impl Module {
&mut self, &mut self,
name: impl Into<String>, name: impl Into<String>,
func: impl Fn(A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]); let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]); let b = cast_arg::<B>(&mut args[1]);
@ -1346,7 +1349,7 @@ impl Module {
name: impl Into<String>, name: impl Into<String>,
namespace: FnNamespace, namespace: FnNamespace,
func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 { ) -> NonZeroU64 {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[1]); let b = cast_arg::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]); let c = cast_arg::<C>(&mut args[2]);
@ -1373,48 +1376,43 @@ impl Module {
/// Get a Rust function. /// 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. /// It is also returned by the `set_fn_XXX` calls.
#[inline(always)] #[inline(always)]
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> { pub(crate) fn get_fn(
if hash_fn == 0 { &self,
None hash_fn: NonZeroU64,
} else { public_only: bool,
self.functions ) -> Option<&CallableFunction> {
.get(&hash_fn) self.functions
.and_then(|FuncInfo { access, func, .. }| match access { .get(&hash_fn)
_ if !public_only => Some(func), .and_then(|FuncInfo { access, func, .. }| match access {
FnAccess::Public => Some(func), _ if !public_only => Some(func),
FnAccess::Private => None, FnAccess::Public => Some(func),
}) FnAccess::Private => None,
} })
} }
/// Does the particular namespace-qualified function exist in the module? /// 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]. /// the hash calculated by [`build_index`][Module::build_index].
#[inline] #[inline(always)]
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool { pub fn contains_qualified_fn(&self, hash_fn: NonZeroU64) -> bool {
if hash_fn == 0 { self.all_functions.contains_key(&hash_fn)
false
} else {
self.all_functions.contains_key(&hash_fn)
}
} }
/// Get a namespace-qualified function. /// Get a namespace-qualified function.
/// Name and Position in `EvalAltResult` are None and must be set afterwards. /// 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]. /// the hash calculated by [`build_index`][Module::build_index].
#[inline(always)] #[inline(always)]
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> { pub(crate) fn get_qualified_fn(
if hash_qualified_fn == 0 { &self,
None hash_qualified_fn: NonZeroU64,
} else { ) -> Option<&CallableFunction> {
self.all_functions.get(&hash_qualified_fn) self.all_functions.get(&hash_qualified_fn)
}
} }
/// Combine another module into this module. /// Combine another module into this module.
@ -1760,8 +1758,8 @@ impl Module {
fn index_module<'a>( fn index_module<'a>(
module: &'a Module, module: &'a Module,
qualifiers: &mut Vec<&'a str>, qualifiers: &mut Vec<&'a str>,
variables: &mut HashMap<u64, Dynamic, StraightHasherBuilder>, variables: &mut HashMap<NonZeroU64, Dynamic, StraightHasherBuilder>,
functions: &mut HashMap<u64, CallableFunction, StraightHasherBuilder>, functions: &mut HashMap<NonZeroU64, CallableFunction, StraightHasherBuilder>,
type_iterators: &mut HashMap<TypeId, IteratorFn>, type_iterators: &mut HashMap<TypeId, IteratorFn>,
) { ) {
module.modules.iter().for_each(|(name, m)| { module.modules.iter().for_each(|(name, m)| {
@ -1775,7 +1773,7 @@ impl Module {
module.variables.iter().for_each(|(var_name, value)| { module.variables.iter().for_each(|(var_name, value)| {
// Qualifiers + variable name // Qualifiers + variable name
let hash_var = 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()); variables.insert(hash_var, value.clone());
}); });
@ -1808,7 +1806,8 @@ impl Module {
// Qualifiers + function name + number of arguments. // Qualifiers + function name + number of arguments.
let hash_qualified_script = 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() { if !func.is_script() {
assert_eq!(*params, param_types.len()); assert_eq!(*params, param_types.len());
@ -1822,9 +1821,12 @@ impl Module {
empty(), empty(),
"", "",
param_types.iter().cloned(), param_types.iter().cloned(),
); )
.unwrap();
// 3) The final hash is the XOR of the two hashes. // 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()); functions.insert(hash_qualified_fn, func.clone());
} else if cfg!(not(feature = "no_function")) { } 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. /// _(INTERNALS)_ A chain of [module][Module] names to namespace-qualify a variable or function call.
/// Exported under the `internals` feature only. /// 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, /// 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. /// and it is wasteful to always allocate a [`Vec`] with one element.

View File

@ -138,7 +138,7 @@ fn call_fn_with_constant_arguments(
&mut Default::default(), &mut Default::default(),
state.lib, state.lib,
fn_name, fn_name,
hash_fn, hash_fn.unwrap(),
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(), arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
false, false,
true, true,

View File

@ -32,7 +32,7 @@ mod fn_ptr_functions {
} else { } else {
let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize); let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize);
ctx.engine() ctx.engine()
.has_override(ctx.mods, ctx.lib, 0, hash_script, true) .has_override(ctx.mods, ctx.lib, None, hash_script, true)
} }
} }
} }

View File

@ -13,7 +13,7 @@ use crate::stdlib::{
format, format,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
iter::empty, iter::empty,
num::NonZeroUsize, num::{NonZeroU64, NonZeroUsize},
string::{String, ToString}, string::{String, ToString},
vec, vec,
vec::Vec, vec::Vec,
@ -34,7 +34,7 @@ use crate::FnAccess;
type PERR = ParseErrorType; type PERR = ParseErrorType;
type FunctionsLib = HashMap<u64, ScriptFnDef, StraightHasherBuilder>; type FunctionsLib = HashMap<NonZeroU64, ScriptFnDef, StraightHasherBuilder>;
/// A type that encapsulates the current state of the parser. /// A type that encapsulates the current state of the parser.
#[derive(Debug)] #[derive(Debug)]
@ -335,7 +335,7 @@ fn parse_fn_call(
Token::RightParen => { Token::RightParen => {
eat_token(input, 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"))] #[cfg(not(feature = "no_module"))]
modules.set_index(state.find_module(&modules[0].name)); modules.set_index(state.find_module(&modules[0].name));
@ -352,13 +352,17 @@ fn parse_fn_call(
calc_script_fn_hash(empty(), &id, 0) 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( return Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: id.to_string().into(), name: id.to_string().into(),
native_only: !is_valid_identifier(id.chars()), // script functions can only be valid identifiers
capture, capture,
namespace, namespace,
hash: hash_script, hash_script,
args, args,
..Default::default() ..Default::default()
}), }),
@ -383,7 +387,7 @@ fn parse_fn_call(
(Token::RightParen, _) => { (Token::RightParen, _) => {
eat_token(input, 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"))] #[cfg(not(feature = "no_module"))]
modules.set_index(state.find_module(&modules[0].name)); 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()) 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( return Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: id.to_string().into(), name: id.to_string().into(),
native_only: !is_valid_identifier(id.chars()), // script functions can only be valid identifiers
capture, capture,
namespace, namespace,
hash: hash_script, hash_script,
args, args,
..Default::default() ..Default::default()
}), }),
@ -973,7 +981,7 @@ fn parse_primary(
name: state.get_interned_string(s), name: state.get_interned_string(s),
pos: settings.pos, 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 // Namespace qualification
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -987,7 +995,7 @@ fn parse_primary(
name: state.get_interned_string(s), name: state.get_interned_string(s),
pos: settings.pos, 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 // Normal variable access
Token::Identifier(s) => { Token::Identifier(s) => {
@ -996,7 +1004,7 @@ fn parse_primary(
name: state.get_interned_string(s), name: state.get_interned_string(s),
pos: settings.pos, 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 // Function call is allowed to have reserved keyword
@ -1006,7 +1014,7 @@ fn parse_primary(
name: state.get_interned_string(s), name: state.get_interned_string(s),
pos: settings.pos, pos: settings.pos,
}; };
Expr::Variable(Box::new((None, None, 0, var_name_def))) Expr::Variable(Box::new((None, None, None, var_name_def)))
} else { } else {
return Err(PERR::Reserved(s).into_err(settings.pos)); return Err(PERR::Reserved(s).into_err(settings.pos));
} }
@ -1022,7 +1030,7 @@ fn parse_primary(
name: state.get_interned_string(s), name: state.get_interned_string(s),
pos: settings.pos, 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), name: state.get_interned_string(id2),
pos: pos2, 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()) => { (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
return Err(PERR::Reserved(id2).into_err(pos2)); return Err(PERR::Reserved(id2).into_err(pos2));
@ -1208,16 +1216,12 @@ fn parse_unary(
// Call negative function // Call negative function
expr => { expr => {
let op = "-"; let op = "-";
let hash = calc_script_fn_hash(empty(), op, 1);
let mut args = StaticVec::new(); let mut args = StaticVec::new();
args.push(expr); args.push(expr);
Ok(Expr::FnCall( Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: op.into(), name: op.into(),
native_only: true,
namespace: None,
hash,
args, args,
..Default::default() ..Default::default()
}), }),
@ -1238,16 +1242,12 @@ fn parse_unary(
// Call plus function // Call plus function
expr => { expr => {
let op = "+"; let op = "+";
let hash = calc_script_fn_hash(empty(), op, 1);
let mut args = StaticVec::new(); let mut args = StaticVec::new();
args.push(expr); args.push(expr);
Ok(Expr::FnCall( Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: op.into(), name: op.into(),
native_only: true,
namespace: None,
hash,
args, args,
..Default::default() ..Default::default()
}), }),
@ -1264,13 +1264,10 @@ fn parse_unary(
args.push(expr); args.push(expr);
let op = "!"; let op = "!";
let hash = calc_script_fn_hash(empty(), op, 1);
Ok(Expr::FnCall( Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: op.into(), name: op.into(),
native_only: true,
hash,
args, args,
def_value: Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false def_value: Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
..Default::default() ..Default::default()
@ -1310,7 +1307,7 @@ fn parse_unary(
}); });
// Qualifiers (none) + function name + number of arguments. // 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); lib.insert(hash, func);
@ -1723,11 +1720,9 @@ fn parse_binary_op(
let cmp_def = Some(false.into()); let cmp_def = Some(false.into());
let op = op_token.syntax(); let op = op_token.syntax();
let hash = calc_script_fn_hash(empty(), &op, 2);
let op_base = FnCallExpr { let op_base = FnCallExpr {
name: op, name: op,
native_only: true,
capture: false, capture: false,
..Default::default() ..Default::default()
}; };
@ -1747,19 +1742,11 @@ fn parse_binary_op(
| Token::PowerOf | Token::PowerOf
| Token::Ampersand | Token::Ampersand
| Token::Pipe | Token::Pipe
| Token::XOr => Expr::FnCall( | Token::XOr => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos),
Box::new(FnCallExpr {
hash,
args,
..op_base
}),
pos,
),
// '!=' defaults to true when passed invalid operands // '!=' defaults to true when passed invalid operands
Token::NotEqualsTo => Expr::FnCall( Token::NotEqualsTo => Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
hash,
args, args,
def_value: Some(true.into()), def_value: Some(true.into()),
..op_base ..op_base
@ -1774,7 +1761,6 @@ fn parse_binary_op(
| Token::GreaterThan | Token::GreaterThan
| Token::GreaterThanEqualsTo => Expr::FnCall( | Token::GreaterThanEqualsTo => Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
hash,
args, args,
def_value: cmp_def, def_value: cmp_def,
..op_base ..op_base
@ -1821,9 +1807,8 @@ fn parse_binary_op(
// Accept non-native functions for custom operators // Accept non-native functions for custom operators
Expr::FnCall( Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
hash, hash_script: calc_script_fn_hash(empty(), &s, 2),
args, args,
native_only: false,
..op_base ..op_base
}), }),
pos, pos,
@ -1892,7 +1877,7 @@ fn parse_custom_syntax(
segments.push(name.clone()); segments.push(name.clone());
tokens.push(state.get_interned_string(MARKER_IDENT)); tokens.push(state.get_interned_string(MARKER_IDENT));
let var_name_def = Ident { name, pos }; 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()) => { (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos)); 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)?; let func = parse_fn(input, &mut new_state, lib, access, settings, comments)?;
// Qualifiers (none) + function name + number of arguments. // 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); lib.insert(hash, func);
@ -2812,7 +2797,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
externals.iter().for_each(|x| { 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")] #[cfg(feature = "no_closure")]
@ -2822,12 +2812,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
let curry_func = crate::engine::KEYWORD_FN_PTR_CURRY; 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( let expr = Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: curry_func.into(), name: curry_func.into(),
hash, hash_script,
args, args,
..Default::default() ..Default::default()
}), }),

View File

@ -9,30 +9,31 @@ use crate::stdlib::{
fmt, fmt,
hash::{BuildHasher, Hash, Hasher}, hash::{BuildHasher, Hash, Hasher},
iter::{empty, FromIterator}, iter::{empty, FromIterator},
num::NonZeroU64,
ops::{Add, AddAssign, Deref}, ops::{Add, AddAssign, Deref},
str::FromStr, str::FromStr,
string::{String, ToString}, string::{String, ToString},
}; };
use crate::Shared; 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
/// ///
/// Panics when hashing any data type other than a [`u64`]. /// Panics when hashing any data type other than a [`NonZeroU64`].
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct StraightHasher(u64); pub struct StraightHasher(NonZeroU64);
impl Hasher for StraightHasher { impl Hasher for StraightHasher {
#[inline(always)] #[inline(always)]
fn finish(&self) -> u64 { fn finish(&self) -> u64 {
self.0 self.0.get()
} }
#[inline(always)] #[inline(always)]
fn write(&mut self, bytes: &[u8]) { fn write(&mut self, bytes: &[u8]) {
let mut key = [0_u8; 8]; let mut key = [0_u8; 8];
key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes 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)] #[inline(always)]
fn build_hasher(&self) -> Self::Hasher { 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. /// Exported under the `internals` feature only.
/// ///
/// Module names are passed in via `&str` references from an iterator. /// 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<Item = &'a str>, modules: impl Iterator<Item = &'a str>,
fn_name: &str, fn_name: &str,
params: impl Iterator<Item = TypeId>, params: impl Iterator<Item = TypeId>,
) -> u64 { ) -> Option<NonZeroU64> {
calc_fn_hash(modules, fn_name, None, params) 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. /// and the number of parameters, but no parameter types.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
@ -82,7 +83,7 @@ pub fn calc_script_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>, modules: impl Iterator<Item = &'a str>,
fn_name: &str, fn_name: &str,
num: usize, num: usize,
) -> u64 { ) -> Option<NonZeroU64> {
calc_fn_hash(modules, fn_name, Some(num), empty()) calc_fn_hash(modules, fn_name, Some(num), empty())
} }
@ -96,7 +97,7 @@ pub fn get_hasher() -> impl Hasher {
s 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. /// Module names are passed in via `&str` references from an iterator.
/// Parameter types are passed in via [`TypeId`] values 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, fn_name: &str,
num: Option<usize>, num: Option<usize>,
params: impl Iterator<Item = TypeId>, params: impl Iterator<Item = TypeId>,
) -> u64 { ) -> Option<NonZeroU64> {
let s = &mut get_hasher(); let s = &mut get_hasher();
// Hash a boolean indicating whether the hash is namespace-qualified. // Hash a boolean indicating whether the hash is namespace-qualified.
@ -122,7 +123,7 @@ fn calc_fn_hash<'a>(
} else { } else {
params.for_each(|t| t.hash(s)); params.for_each(|t| t.hash(s));
} }
s.finish() NonZeroU64::new(s.finish())
} }
/// The system immutable string type. /// The system immutable string type.