Add FnNamespace for module functions.

This commit is contained in:
Stephen Chung
2020-11-17 12:23:53 +08:00
parent a19865d811
commit 038e3c2554
20 changed files with 264 additions and 171 deletions

View File

@@ -30,11 +30,44 @@ use crate::Array;
#[cfg(not(feature = "no_object"))]
use crate::Map;
#[cfg(not(feature = "no_function"))]
pub type SharedScriptFnDef = Shared<crate::ast::ScriptFnDef>;
/// A type representing the namespace of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnNamespace {
/// Global namespace.
Global,
/// Internal only.
Internal,
}
impl FnNamespace {
/// Is this namespace global?
#[inline(always)]
pub fn is_global(self) -> bool {
match self {
Self::Global => true,
Self::Internal => false,
}
}
/// Is this namespace internal?
#[inline(always)]
pub fn is_internal(self) -> bool {
match self {
Self::Global => false,
Self::Internal => true,
}
}
}
/// Data structure containing a single registered function.
#[derive(Debug, Clone)]
pub struct FuncInfo {
/// Function instance.
pub func: CallableFunction,
/// Function namespace.
pub namespace: FnNamespace,
/// Function access mode.
pub access: FnAccess,
/// Function name.
@@ -281,7 +314,7 @@ 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: Shared<crate::ast::ScriptFnDef>) -> u64 {
pub(crate) fn set_script_fn(&mut self, fn_def: SharedScriptFnDef) -> u64 {
// 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);
@@ -289,6 +322,7 @@ impl Module {
hash_script,
FuncInfo {
name: fn_def.name.to_string(),
namespace: FnNamespace::Internal,
access: fn_def.access,
params: num_params,
types: None,
@@ -307,7 +341,7 @@ impl Module {
name: &str,
num_params: usize,
public_only: bool,
) -> Option<&Shared<crate::ast::ScriptFnDef>> {
) -> Option<&SharedScriptFnDef> {
self.functions
.values()
.find(
@@ -439,6 +473,7 @@ impl Module {
pub fn set_fn(
&mut self,
name: impl Into<String>,
namespace: FnNamespace,
access: FnAccess,
arg_types: &[TypeId],
func: CallableFunction,
@@ -463,6 +498,7 @@ impl Module {
hash_fn,
FuncInfo {
name,
namespace,
access,
params: params.len(),
types: Some(params),
@@ -506,10 +542,11 @@ impl Module {
/// # Example
///
/// ```
/// use rhai::Module;
/// use rhai::{Module, FnNamespace, FnAccess};
///
/// let mut module = Module::new();
/// let hash = module.set_raw_fn("double_or_not",
/// FnNamespace::Internal, FnAccess::Public,
/// // Pass parameter types via a slice with TypeId's
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
/// // Fixed closure signature
@@ -538,6 +575,8 @@ impl Module {
pub fn set_raw_fn<T: Variant + Clone>(
&mut self,
name: impl Into<String>,
namespace: FnNamespace,
access: FnAccess,
arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync
@@ -545,14 +584,40 @@ impl Module {
) -> u64 {
let f =
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
self.set_fn(
name,
FnAccess::Public,
namespace,
access,
arg_types,
CallableFunction::from_method(Box::new(f)),
)
}
/// Get the namespace of a registered function.
/// Returns `None` if a function with the hash does not exist.
///
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`.
#[inline(always)]
pub fn get_fn_namespace(&self, hash: u64) -> Option<FnNamespace> {
self.functions.get(&hash).map(|f| f.namespace)
}
/// Set the namespace of a registered function.
/// Returns the original namespace or `None` if a function with the hash does not exist.
///
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`.
#[inline]
pub fn set_fn_namespace(&mut self, hash: u64, namespace: FnNamespace) -> Option<FnNamespace> {
if let Some(f) = self.functions.get_mut(&hash) {
let old_ns = f.namespace;
f.namespace = namespace;
Some(old_ns)
} else {
None
}
}
/// Set a Rust function taking no parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
@@ -576,6 +641,7 @@ impl Module {
let arg_types = [];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_pure(Box::new(f)),
@@ -607,6 +673,7 @@ impl Module {
let arg_types = [TypeId::of::<A>()];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_pure(Box::new(f)),
@@ -638,6 +705,7 @@ impl Module {
let arg_types = [TypeId::of::<A>()];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_method(Box::new(f)),
@@ -697,6 +765,7 @@ impl Module {
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_pure(Box::new(f)),
@@ -734,6 +803,7 @@ impl Module {
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_method(Box::new(f)),
@@ -847,6 +917,7 @@ impl Module {
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_pure(Box::new(f)),
@@ -890,6 +961,7 @@ impl Module {
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_method(Box::new(f)),
@@ -948,6 +1020,7 @@ impl Module {
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn(
crate::engine::FN_IDX_SET,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_method(Box::new(f)),
@@ -1038,6 +1111,7 @@ impl Module {
];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_pure(Box::new(f)),
@@ -1088,6 +1162,7 @@ impl Module {
];
self.set_fn(
name,
FnNamespace::Internal,
FnAccess::Public,
&arg_types,
CallableFunction::from_method(Box::new(f)),
@@ -1195,14 +1270,14 @@ impl Module {
/// Merge another module into this module.
#[inline(always)]
pub fn merge(&mut self, other: &Self) -> &mut Self {
self.merge_filtered(other, &mut |_, _, _| true)
self.merge_filtered(other, &mut |_, _, _, _, _| true)
}
/// Merge another module into this module, with only selected script-defined functions based on a filter predicate.
/// Merge another module into this module based on a filter predicate.
pub(crate) fn merge_filtered(
&mut self,
other: &Self,
mut _filter: &mut impl FnMut(FnAccess, &str, usize) -> bool,
mut _filter: &mut impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> &mut Self {
#[cfg(not(feature = "no_function"))]
other.modules.iter().for_each(|(k, v)| {
@@ -1220,13 +1295,27 @@ impl Module {
other
.functions
.iter()
.filter(|(_, FuncInfo { func, .. })| match func {
#[cfg(not(feature = "no_function"))]
CallableFunction::Script(f) => {
_filter(f.access, f.name.as_str(), f.params.len())
}
_ => true,
})
.filter(
|(
_,
FuncInfo {
namespace,
access,
name,
params,
func,
..
},
)| {
_filter(
*namespace,
*access,
func.is_script(),
name.as_str(),
*params,
)
},
)
.map(|(&k, v)| (k, v.clone())),
);
@@ -1238,18 +1327,30 @@ impl Module {
self
}
/// Filter out the functions, retaining only some based on a filter predicate.
/// Filter out the functions, retaining only some script-defined functions based on a filter predicate.
#[cfg(not(feature = "no_function"))]
#[inline]
pub(crate) fn retain_functions(
pub(crate) fn retain_script_functions(
&mut self,
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
mut filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool,
) -> &mut Self {
self.functions
.retain(|_, FuncInfo { func, .. }| match func {
CallableFunction::Script(f) => filter(f.access, f.name.as_str(), f.params.len()),
_ => true,
});
self.functions.retain(
|_,
FuncInfo {
namespace,
access,
name,
params,
func,
..
}| {
if func.is_script() {
filter(*namespace, *access, name.as_str(), *params)
} else {
false
}
},
);
self.all_functions.clear();
self.all_variables.clear();
@@ -1293,16 +1394,25 @@ impl Module {
#[inline(always)]
pub(crate) fn iter_script_fn<'a>(
&'a self,
) -> impl Iterator<Item = (FnAccess, &str, usize, Shared<crate::ast::ScriptFnDef>)> + 'a {
self.functions
.values()
.map(|f| &f.func)
.filter(|f| f.is_script())
.map(CallableFunction::get_fn_def)
.map(|f| {
let func = f.clone();
(f.access, f.name.as_str(), f.params.len(), func)
})
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, SharedScriptFnDef)> + 'a {
self.functions.values().filter(|f| f.func.is_script()).map(
|FuncInfo {
namespace,
access,
name,
params,
func,
..
}| {
(
*namespace,
*access,
name.as_str(),
*params,
func.get_fn_def().clone(),
)
},
)
}
/// Get an iterator over all script-defined functions in the module.
@@ -1314,14 +1424,17 @@ impl Module {
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "internals"))]
#[inline(always)]
pub fn iter_script_fn_info(&self) -> impl Iterator<Item = (FnAccess, &str, usize)> {
pub fn iter_script_fn_info(
&self,
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize)> {
self.functions.values().filter(|f| f.func.is_script()).map(
|FuncInfo {
name,
namespace,
access,
params,
..
}| (*access, name.as_str(), *params),
}| (*namespace, *access, name.as_str(), *params),
)
}
@@ -1338,7 +1451,7 @@ impl Module {
#[inline(always)]
pub fn iter_script_fn_info(
&self,
) -> impl Iterator<Item = (FnAccess, &str, usize, Shared<crate::ast::ScriptFnDef>)> {
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, SharedScriptFnDef)> {
self.iter_script_fn()
}
@@ -1392,13 +1505,10 @@ impl Module {
// Extra modules left in the scope become sub-modules
let mut func_mods: crate::engine::Imports = Default::default();
mods.into_iter()
.skip(orig_mods_len)
.filter(|&(_, fixed, _)| !fixed)
.for_each(|(alias, _, m)| {
func_mods.push(alias.clone(), m.clone());
module.set_sub_module(alias, m);
});
mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| {
func_mods.push(alias.clone(), m.clone());
module.set_sub_module(alias, m);
});
// Non-private functions defined become module functions
#[cfg(not(feature = "no_function"))]
@@ -1406,8 +1516,8 @@ impl Module {
let ast_lib: Shared<Module> = ast.lib().clone().into();
ast.iter_functions()
.filter(|(access, _, _, _)| !access.is_private())
.for_each(|(_, _, _, func)| {
.filter(|(_, access, _, _, _)| !access.is_private())
.for_each(|(_, _, _, _, func)| {
// Encapsulate AST environment
let mut func = func.as_ref().clone();
func.lib = Some(ast_lib.clone());
@@ -1463,6 +1573,7 @@ impl Module {
&hash,
FuncInfo {
name,
namespace,
params,
types,
func,
@@ -1470,22 +1581,20 @@ impl Module {
},
)| {
// Flatten all methods so they can be available without namespace qualifiers
#[cfg(not(feature = "no_object"))]
if func.is_method() {
if namespace.is_global() {
functions.insert(hash, func.clone());
}
// Qualifiers + function name + number of arguments.
let hash_qualified_script =
crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params);
if let Some(param_types) = types {
assert_eq!(*params, param_types.len());
// Namespace-qualified Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + number of arguments.
let hash_qualified_script = crate::calc_script_fn_hash(
qualifiers.iter().cloned(),
name,
*params,
);
// 2) Calculate a second hash with no qualifiers, empty function name,
// and the actual list of argument `TypeId`'.s
let hash_fn_args = crate::calc_native_fn_hash(
@@ -1498,17 +1607,6 @@ impl Module {
functions.insert(hash_qualified_fn, func.clone());
} else if cfg!(not(feature = "no_function")) {
let hash_qualified_script =
if cfg!(feature = "no_object") && qualifiers.is_empty() {
hash
} else {
// Qualifiers + function name + number of arguments.
crate::calc_script_fn_hash(
qualifiers.iter().map(|&v| v),
&name,
*params,
)
};
functions.insert(hash_qualified_script, func.clone());
}
},