Add Dynamic parameters to qualified function calls.

This commit is contained in:
Stephen Chung 2022-04-08 11:27:52 +08:00
parent 6481e19673
commit 42f977862f
3 changed files with 74 additions and 3 deletions

View File

@ -1,6 +1,15 @@
Rhai Release Notes Rhai Release Notes
================== ==================
Version 1.7.0
=============
Bug fixes
---------
* Functions with `Dynamic` parameters now work in qualified calls from `import`ed modules.
Version 1.6.0 Version 1.6.0
============= =============

View File

@ -1301,13 +1301,14 @@ impl Engine {
} }
} }
// Search for the root namespace
let module = self let module = self
.search_imports(global, state, namespace) .search_imports(global, state, namespace)
.ok_or_else(|| ERR::ErrorModuleNotFound(namespace.to_string(), namespace.position()))?; .ok_or_else(|| ERR::ErrorModuleNotFound(namespace.to_string(), namespace.position()))?;
// First search in script-defined functions (can override built-in) // First search script-defined functions in namespace (can override built-in)
let func = match module.get_qualified_fn(hash) { let mut func = match module.get_qualified_fn(hash) {
// Then search in Rust functions // Then search native Rust functions
None => { None => {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, pos)?; self.inc_operations(&mut global.num_operations, pos)?;
@ -1320,6 +1321,41 @@ impl Engine {
r => r, r => r,
}; };
// Check for `Dynamic` parameters.
//
// Note - This is done during every function call mismatch without cache,
// so hopefully the number of arguments should not be too many
// (expected because closures cannot be qualified).
if func.is_none() && !args.is_empty() {
let num_args = args.len();
let max_bitmask = 1usize << usize::min(num_args, MAX_DYNAMIC_PARAMETERS);
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
// Try all permutations with `Dynamic` wildcards
while bitmask < max_bitmask {
let hash_params = calc_fn_params_hash(args.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()
}
}));
let hash_qualified_fn = combine_hashes(hash, hash_params);
#[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, pos)?;
if let Some(f) = module.get_qualified_fn(hash_qualified_fn) {
func = Some(f);
break;
}
bitmask += 1;
}
}
// Clone first argument if the function is not a method after-all // Clone first argument if the function is not a method after-all
if !func.map(|f| f.is_method()).unwrap_or(true) { if !func.map(|f| f.is_method()).unwrap_or(true) {
if let Some(first) = first_arg_value { if let Some(first) = first_arg_value {

View File

@ -562,3 +562,29 @@ fn test_module_environ() -> Result<(), Box<EvalAltResult>> {
Ok(()) Ok(())
} }
#[test]
fn test_module_dynamic() -> Result<(), Box<EvalAltResult>> {
fn test_fn(input: Dynamic, x: INT) -> Result<INT, Box<EvalAltResult>> {
let s = input.into_string().unwrap();
Ok(s.len() as INT + x)
}
let mut engine = rhai::Engine::new();
let mut module = Module::new();
module.set_native_fn("test", test_fn);
let mut static_modules = rhai::module_resolvers::StaticModuleResolver::new();
static_modules.insert("test", module);
engine.set_module_resolver(static_modules);
engine.register_result_fn("test2", test_fn);
assert_eq!(engine.eval::<INT>(r#"test2("test", 38);"#)?, 42);
assert_eq!(
engine.eval::<INT>(r#"import "test" as test; test::test("test", 38);"#)?,
42
);
Ok(())
}