fn resolve_fn()

in rhai/src/func/call.rs [163:327]


    fn resolve_fn<'s>(
        &self,
        _global: &GlobalRuntimeState,
        caches: &'s mut Caches,
        local_entry: &'s mut Option<FnResolutionCacheEntry>,
        op_token: Option<&Token>,
        hash_base: u64,
        args: Option<&mut FnCallArgs>,
        allow_dynamic: bool,
    ) -> Option<&'s FnResolutionCacheEntry> {
        let mut hash = args.as_deref().map_or(hash_base, |args| {
            calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id()))
        });

        let cache = caches.fn_resolution_cache_mut();

        match cache.map.entry(hash) {
            Entry::Occupied(entry) => entry.into_mut().as_ref(),
            Entry::Vacant(entry) => {
                let num_args = args.as_deref().map_or(0, FnCallArgs::len);
                let mut max_bitmask = 0; // One above maximum bitmask based on number of parameters.
                                         // Set later when a specific matching function is not found.
                let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`

                loop {
                    #[cfg(not(feature = "no_function"))]
                    let func = _global
                        .lib
                        .iter()
                        .rev()
                        .chain(self.global_modules.iter())
                        .find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw())));
                    #[cfg(feature = "no_function")]
                    let func = None;

                    let func = func.or_else(|| {
                        self.global_modules
                            .iter()
                            .find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw())))
                    });

                    #[cfg(not(feature = "no_module"))]
                    let func = func
                        .or_else(|| _global.get_qualified_fn(hash, true))
                        .or_else(|| {
                            self.global_sub_modules
                                .as_ref()
                                .into_iter()
                                .flatten()
                                .filter(|(_, m)| m.contains_indexed_global_functions())
                                .find_map(|(_, m)| {
                                    m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))
                                })
                        });

                    if let Some((f, s)) = func {
                        // Specific version found
                        let new_entry = FnResolutionCacheEntry {
                            func: f.clone(),
                            source: s.cloned(),
                        };
                        return if cache.filter.is_absent_and_set(hash) {
                            // Do not cache "one-hit wonders"
                            *local_entry = Some(new_entry);
                            local_entry.as_ref()
                        } else {
                            // Cache entry
                            entry.insert(Some(new_entry)).as_ref()
                        };
                    }

                    // Check `Dynamic` parameters for functions with parameters
                    if allow_dynamic && max_bitmask == 0 && num_args > 0 {
                        let is_dynamic = self
                            .global_modules
                            .iter()
                            .any(|m| m.may_contain_dynamic_fn(hash_base));

                        #[cfg(not(feature = "no_function"))]
                        let is_dynamic = is_dynamic
                            || _global
                                .lib
                                .iter()
                                .any(|m| m.may_contain_dynamic_fn(hash_base));

                        #[cfg(not(feature = "no_module"))]
                        let is_dynamic = is_dynamic
                            || _global.may_contain_dynamic_fn(hash_base)
                            || self.global_sub_modules.as_ref().map_or(false, |m| {
                                m.values().any(|m| m.may_contain_dynamic_fn(hash_base))
                            });

                        // Set maximum bitmask when there are dynamic versions of the function
                        if is_dynamic {
                            max_bitmask = 1usize << usize::min(num_args, MAX_DYNAMIC_PARAMETERS);
                        }
                    }

                    // Stop when all permutations are exhausted
                    if bitmask >= max_bitmask {
                        if num_args != 2 {
                            return None;
                        }

                        // Try to find a built-in version
                        let builtin =
                            args.and_then(|args| match op_token {
                                None => None,
                                Some(token) if token.is_op_assignment() => {
                                    let (first_arg, rest_args) = args.split_first().unwrap();

                                    get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
                                        .map(|(f, has_context)| FnResolutionCacheEntry {
                                            func: CallableFunction::Method {
                                                func: Shared::new(f),
                                                has_context,
                                                is_pure: false,
                                            },
                                            source: None,
                                        })
                                }
                                Some(token) => get_builtin_binary_op_fn(token, args[0], args[1])
                                    .map(|(f, has_context)| FnResolutionCacheEntry {
                                        func: CallableFunction::Method {
                                            func: Shared::new(f),
                                            has_context,
                                            is_pure: true,
                                        },
                                        source: None,
                                    }),
                            });

                        return if cache.filter.is_absent_and_set(hash) {
                            // Do not cache "one-hit wonders"
                            *local_entry = builtin;
                            local_entry.as_ref()
                        } else {
                            // Cache entry
                            entry.insert(builtin).as_ref()
                        };
                    }

                    // Try all permutations with `Dynamic` wildcards
                    hash = calc_fn_hash_full(
                        hash_base,
                        args.as_ref()
                            .expect("no permutations")
                            .iter()
                            .enumerate()
                            .map(|(i, a)| {
                                let mask = 1usize << (num_args - i - 1);
                                if bitmask & mask == 0 {
                                    a.type_id()
                                } else {
                                    // Replace with `Dynamic`
                                    TypeId::of::<Dynamic>()
                                }
                            }),
                    );

                    bitmask += 1;
                }
            }
        }
    }