Detect whether dynamic functions exist to save checking.

This commit is contained in:
Stephen Chung 2022-06-24 11:30:54 +08:00
parent d6bfd8f617
commit 84b8e1ed87
3 changed files with 130 additions and 63 deletions

View File

@ -165,7 +165,7 @@ impl Engine {
) )
} }
/// Resolve a function call. /// Resolve a normal (non-qualified) function call.
/// ///
/// Search order: /// Search order:
/// 1) AST - script functions in the AST /// 1) AST - script functions in the AST
@ -201,11 +201,8 @@ impl Engine {
.entry(hash) .entry(hash)
.or_insert_with(|| { .or_insert_with(|| {
let num_args = args.as_ref().map_or(0, |a| a.len()); let num_args = args.as_ref().map_or(0, |a| a.len());
let max_bitmask = if !allow_dynamic { let mut max_bitmask = 0; // One above maximum bitmask based on number of parameters.
0 // Set later when a specific matching function is not found.
} else {
1usize << usize::min(num_args, MAX_DYNAMIC_PARAMETERS)
};
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic` let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
loop { loop {
@ -247,62 +244,85 @@ impl Engine {
}) })
}); });
match func { // Specific version found
// Specific version found if let Some(f) = func {
Some(f) => return Some(f), return Some(f);
}
// Stop when all permutations are exhausted // Check `Dynamic` parameters for functions with parameters
None if bitmask >= max_bitmask => { if allow_dynamic && max_bitmask == 0 && num_args > 0 {
if num_args != 2 { let is_dynamic = lib.iter().any(|&m| m.contains_dynamic_fn(hash_script))
return None; || self
} .global_modules
.iter()
.any(|m| m.contains_dynamic_fn(hash_script));
return args.and_then(|args| { #[cfg(not(feature = "no_module"))]
if !is_op_assignment { let is_dynamic = is_dynamic
get_builtin_binary_op_fn(fn_name, &args[0], &args[1]).map(|f| { || _global
FnResolutionCacheEntry { .iter_imports_raw()
func: CallableFunction::from_method( .any(|(_, m)| m.contains_dynamic_fn(hash_script))
Box::new(f) as Box<FnAny> || self
), .global_sub_modules
source: None, .values()
} .any(|m| m.contains_dynamic_fn(hash_script));
})
} else {
let (first_arg, rest_args) = args.split_first().unwrap();
get_builtin_op_assignment_fn(fn_name, *first_arg, rest_args[0]) // Set maximum bitmask when there are dynamic versions of the function
.map(|f| FnResolutionCacheEntry { if is_dynamic {
func: CallableFunction::from_method( max_bitmask = 1usize << usize::min(num_args, MAX_DYNAMIC_PARAMETERS);
Box::new(f) as Box<FnAny>
),
source: None,
})
}
});
}
// Try all permutations with `Dynamic` wildcards
None => {
let hash_params = calc_fn_params_hash(
args.as_ref()
.expect("no permutations")
.iter()
.enumerate()
.map(|(i, a)| {
let mask = 1usize << (num_args - i - 1);
if bitmask & mask != 0 {
// Replace with `Dynamic`
TypeId::of::<Dynamic>()
} else {
a.type_id()
}
}),
);
hash = combine_hashes(hash_script, hash_params);
bitmask += 1;
} }
} }
// Stop when all permutations are exhausted
if bitmask >= max_bitmask {
if num_args != 2 {
return None;
}
return args.and_then(|args| {
if !is_op_assignment {
get_builtin_binary_op_fn(fn_name, &args[0], &args[1]).map(|f| {
FnResolutionCacheEntry {
func: CallableFunction::from_method(
Box::new(f) as Box<FnAny>
),
source: None,
}
})
} else {
let (first_arg, rest_args) = args.split_first().unwrap();
get_builtin_op_assignment_fn(fn_name, *first_arg, rest_args[0]).map(
|f| FnResolutionCacheEntry {
func: CallableFunction::from_method(
Box::new(f) as Box<FnAny>
),
source: None,
},
)
}
});
}
// Try all permutations with `Dynamic` wildcards
let hash_params = calc_fn_params_hash(
args.as_ref()
.expect("no permutations")
.iter()
.enumerate()
.map(|(i, a)| {
let mask = 1usize << (num_args - i - 1);
if bitmask & mask != 0 {
// Replace with `Dynamic`
TypeId::of::<Dynamic>()
} else {
a.type_id()
}
}),
);
hash = combine_hashes(hash_script, hash_params);
bitmask += 1;
} }
}); });

View File

@ -7,7 +7,7 @@ use crate::func::{
}; };
use crate::types::{dynamic::Variant, CustomTypesCollection}; use crate::types::{dynamic::Variant, CustomTypesCollection};
use crate::{ use crate::{
calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier, calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier,
ImmutableString, NativeCallContext, RhaiResultOf, Shared, StaticVec, ImmutableString, NativeCallContext, RhaiResultOf, Shared, StaticVec,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -241,11 +241,13 @@ pub struct Module {
variables: BTreeMap<Identifier, Dynamic>, variables: BTreeMap<Identifier, 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: BTreeMap<u64, Dynamic>, all_variables: BTreeMap<u64, Dynamic>,
/// External Rust functions. /// Functions (both native Rust and scripted).
functions: BTreeMap<u64, Box<FuncInfo>>, functions: BTreeMap<u64, Box<FuncInfo>>,
/// Flattened collection of all external Rust functions, native or scripted. /// Flattened collection of all functions, native Rust and scripted.
/// including those in sub-modules. /// including those in sub-modules.
all_functions: BTreeMap<u64, CallableFunction>, all_functions: BTreeMap<u64, CallableFunction>,
/// Native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters.
dynamic_functions: BTreeSet<u64>,
/// Iterator functions, keyed by the type producing the iterator. /// Iterator functions, keyed by the type producing the iterator.
type_iterators: BTreeMap<TypeId, Shared<IteratorFn>>, type_iterators: BTreeMap<TypeId, Shared<IteratorFn>>,
/// Flattened collection of iterator functions, including those in sub-modules. /// Flattened collection of iterator functions, including those in sub-modules.
@ -348,6 +350,7 @@ impl Module {
all_variables: BTreeMap::new(), all_variables: BTreeMap::new(),
functions: BTreeMap::new(), functions: BTreeMap::new(),
all_functions: BTreeMap::new(), all_functions: BTreeMap::new(),
dynamic_functions: BTreeSet::new(),
type_iterators: BTreeMap::new(), type_iterators: BTreeMap::new(),
all_type_iterators: BTreeMap::new(), all_type_iterators: BTreeMap::new(),
indexed: true, indexed: true,
@ -417,6 +420,25 @@ impl Module {
self self
} }
/// Clear the [`Module`].
#[inline(always)]
pub fn clear(&mut self) {
self.id.clear();
self.internal = false;
self.standard = false;
self.custom_types.clear();
self.modules.clear();
self.variables.clear();
self.all_variables.clear();
self.functions.clear();
self.all_functions.clear();
self.dynamic_functions.clear();
self.type_iterators.clear();
self.all_type_iterators.clear();
self.indexed = false;
self.contains_indexed_global_functions = false;
}
/// Map a custom type to a friendly display name. /// Map a custom type to a friendly display name.
/// ///
/// # Example /// # Example
@ -964,6 +986,10 @@ impl Module {
.collect(); .collect();
param_types.shrink_to_fit(); param_types.shrink_to_fit();
let is_dynamic = param_types
.iter()
.any(|&type_id| type_id == TypeId::of::<Dynamic>());
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
let (param_names, return_type_name) = { let (param_names, return_type_name) = {
let mut names = _arg_names let mut names = _arg_names
@ -982,6 +1008,11 @@ impl Module {
let hash_fn = calc_native_fn_hash(None, name.as_ref(), &param_types); let hash_fn = calc_native_fn_hash(None, name.as_ref(), &param_types);
if is_dynamic {
self.dynamic_functions
.insert(calc_fn_hash(name.as_ref(), param_types.len()));
}
self.functions.insert( self.functions.insert(
hash_fn, hash_fn,
FuncInfo { FuncInfo {
@ -1444,19 +1475,30 @@ impl Module {
) )
} }
/// Get a Rust function. /// Look up a Rust function by hash.
/// ///
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
#[inline] #[inline]
#[must_use] #[must_use]
pub(crate) fn get_fn(&self, hash_fn: u64) -> Option<&CallableFunction> { pub(crate) fn get_fn(&self, hash_native: u64) -> Option<&CallableFunction> {
if !self.functions.is_empty() { if !self.functions.is_empty() {
self.functions.get(&hash_fn).map(|f| &f.func) self.functions.get(&hash_native).map(|f| &f.func)
} else { } else {
None None
} }
} }
/// Does the particular function with [`Dynamic`] parameter(s) exist in the [`Module`]?
#[inline(always)]
#[must_use]
pub(crate) fn contains_dynamic_fn(&self, hash_script: u64) -> bool {
if !self.dynamic_functions.is_empty() {
self.dynamic_functions.contains(&hash_script)
} else {
false
}
}
/// 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 [`build_index`][Module::build_index]. /// The [`u64`] hash is calculated by [`build_index`][Module::build_index].

View File

@ -25,6 +25,11 @@ impl CustomTypesCollection {
pub fn new() -> Self { pub fn new() -> Self {
Self(BTreeMap::new()) Self(BTreeMap::new())
} }
/// Clear the [`CustomTypesCollection`].
#[inline(always)]
pub fn clear(&mut self) {
self.0.clear();
}
/// Register a custom type. /// Register a custom type.
#[inline(always)] #[inline(always)]
pub fn add(&mut self, type_name: impl Into<Identifier>, name: impl Into<Identifier>) { pub fn add(&mut self, type_name: impl Into<Identifier>, name: impl Into<Identifier>) {