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

@ -24,7 +24,7 @@ categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
[dependencies] [dependencies]
smallvec = { version = "1.4.2", default-features = false } smallvec = { version = "1.4.2", default-features = false }
rhai_codegen = { version = "0.2", path = "codegen" } rhai_codegen = { version = "0.3", path = "codegen" }
[features] [features]
default = [] default = []

View File

@ -6,11 +6,16 @@ Version 0.19.6
This version adds the `switch` statement. This version adds the `switch` statement.
It also allows exposing selected module functions (usually methods) to the global namespace.
New features New features
------------ ------------
* `switch` statement. * `switch` statement.
* `Engine::register_module` to register a module as a sub-module in the global namespace, while at the same time exposing its method functions globally. This is convenient when registering an API for a custom type. * `Engine::register_module` to register a module as a sub-module in the global namespace.
* `Module::get_fn_namespace` and `Module::set_fn_namespace` can expose a module function to the global namespace. This is convenient when registering an API for a custom type.
* `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace.
* `#[rhai_fn(gobal)]` and `#[rhai_fn(internal)]` attributes to determine whether a function defined in a plugin module should be exposed to the global namespace. This is convenient when defining an API for a custom type.
Enhancements Enhancements
------------ ------------

View File

@ -3,7 +3,7 @@
{{#include ../links.md}} {{#include ../links.md}}
Iterating through a range or an [array], or any type with a registered _iterator_, Iterating through a range or an [array], or any type with a registered _type iterator_,
is provided by the `for` ... `in` loop. is provided by the `for` ... `in` loop.
Like C, `continue` can be used to skip to the next iteration, by-passing all following statements; Like C, `continue` can be used to skip to the next iteration, by-passing all following statements;

View File

@ -6,7 +6,7 @@ Modules
Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_. Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_.
Modules can be disabled via the [`no_module`] feature. Modules can be disabled via the [`no_module`] feature.
A module is of the type `Module` and holds a collection of functions, variables, iterators and sub-modules. A module is of the type `Module` and holds a collection of functions, variables, type iterators and sub-modules.
It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions
and variables defined by that script. and variables defined by that script.

View File

@ -91,7 +91,7 @@ Many script-oriented exceptions can be caught via `try` ... `catch`:
| [Array]/[string] indexing out-of-bounds | error message [string] | | [Array]/[string] indexing out-of-bounds | error message [string] |
| Indexing with an inappropriate data type | error message [string] | | Indexing with an inappropriate data type | error message [string] |
| Error in a dot expression | error message [string] | | Error in a dot expression | error message [string] |
| `for` statement without an iterator | error message [string] | | `for` statement without an type iterator | error message [string] |
| Error in an `in` expression | error message [string] | | Error in an `in` expression | error message [string] |
| Data race detected | error message [string] | | Data race detected | error message [string] |

View File

@ -37,7 +37,7 @@ mod MyEnumModule {
MyEnum::Baz(val1, val2) MyEnum::Baz(val1, val2)
} }
// Access to fields // Access to fields
#[rhai_fn(get = "enum_type")] #[rhai_fn(global, get = "enum_type")]
pub fn get_type(a: &mut MyEnum) -> String { pub fn get_type(a: &mut MyEnum) -> String {
match a { match a {
MyEnum::Foo => "Foo".to_string(), MyEnum::Foo => "Foo".to_string(),
@ -45,7 +45,7 @@ mod MyEnumModule {
MyEnum::Baz(_, _) => "Baz".to_string() MyEnum::Baz(_, _) => "Baz".to_string()
} }
} }
#[rhai_fn(get = "field_0")] #[rhai_fn(global, get = "field_0")]
pub fn get_field_0(a: &mut MyEnum) -> Dynamic { pub fn get_field_0(a: &mut MyEnum) -> Dynamic {
match a { match a {
MyEnum::Foo => Dynamic::UNIT, MyEnum::Foo => Dynamic::UNIT,
@ -53,7 +53,7 @@ mod MyEnumModule {
MyEnum::Baz(x, _) => Dynamic::from(x) MyEnum::Baz(x, _) => Dynamic::from(x)
} }
} }
#[rhai_fn(get = "field_1")] #[rhai_fn(global, get = "field_1")]
pub fn get_field_1(a: &mut MyEnum) -> Dynamic { pub fn get_field_1(a: &mut MyEnum) -> Dynamic {
match a { match a {
MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT, MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT,
@ -61,41 +61,41 @@ mod MyEnumModule {
} }
} }
// Printing // Printing
#[rhai(name = "to_string", name = "print", name = "debug")] #[rhai(global, name = "to_string", name = "print", name = "debug")]
pub fn to_string(a: &mut MyEnum) -> String { pub fn to_string(a: &mut MyEnum) -> String {
format!("{:?}", a)) format!("{:?}", a))
} }
#[rhai_fn(name = "+")] #[rhai_fn(global, name = "+")]
pub fn add_to_str(s: &str, a: MyEnum) -> String { pub fn add_to_str(s: &str, a: MyEnum) -> String {
format!("{}{:?}", s, a)) format!("{}{:?}", s, a))
} }
#[rhai_fn(name = "+")] #[rhai_fn(global, name = "+")]
pub fn add_str(a: &mut MyEnum, s: &str) -> String { pub fn add_str(a: &mut MyEnum, s: &str) -> String {
format!("{:?}", a).push_str(s)) format!("{:?}", a).push_str(s))
} }
#[rhai_fn(name = "+=")] #[rhai_fn(global, name = "+=")]
pub fn append_to_str(s: &mut ImmutableString, a: MyEnum) -> String { pub fn append_to_str(s: &mut ImmutableString, a: MyEnum) -> String {
s += a.to_string()) s += a.to_string())
} }
// '==' and '!=' operators // '==' and '!=' operators
#[rhai_fn(name = "==")] #[rhai_fn(global, name = "==")]
pub fn eq(a: &mut MyEnum, b: MyEnum) -> bool { pub fn eq(a: &mut MyEnum, b: MyEnum) -> bool {
a == &b a == &b
} }
#[rhai_fn(name = "!=")] #[rhai_fn(global, name = "!=")]
pub fn neq(a: &mut MyEnum, b: MyEnum) -> bool { pub fn neq(a: &mut MyEnum, b: MyEnum) -> bool {
a != &b a != &b
} }
// Array functions // Array functions
#[rhai_fn(name = "push")] #[rhai_fn(global, name = "push")]
pub fn append_to_array(list: &mut Array, item: MyEnum) { pub fn append_to_array(list: &mut Array, item: MyEnum) {
list.push(Dynamic::from(item))); list.push(Dynamic::from(item)));
} }
#[rhai_fn(name = "+=")] #[rhai_fn(global, name = "+=")]
pub fn append_to_array_op(list: &mut Array, item: MyEnum) { pub fn append_to_array_op(list: &mut Array, item: MyEnum) {
list.push(Dynamic::from(item))); list.push(Dynamic::from(item)));
} }
#[rhai_fn(name = "insert")] #[rhai_fn(global, name = "insert")]
pub fn insert_to_array(list: &mut Array, position: i64, item: MyEnum) { pub fn insert_to_array(list: &mut Array, position: i64, item: MyEnum) {
if position <= 0 { if position <= 0 {
list.insert(0, Dynamic::from(item)); list.insert(0, Dynamic::from(item));
@ -105,7 +105,7 @@ mod MyEnumModule {
list.insert(position as usize, Dynamic::from(item)); list.insert(position as usize, Dynamic::from(item));
} }
} }
#[rhai_fn(name = "pad")] #[rhai_fn(global, name = "pad")]
pub fn pad_array(list: &mut Array, len: i64, item: MyEnum) { pub fn pad_array(list: &mut Array, len: i64, item: MyEnum) {
if len as usize > list.len() { list.resize(len as usize, item); } if len as usize > list.len() { list.resize(len as usize, item); }
} }

View File

@ -54,6 +54,8 @@ mod my_module {
mystic_number() mystic_number()
} }
// This function will be registered as 'increment'. // This function will be registered as 'increment'.
// It will also be exposed to the global namespace since 'global' is set.
#[rhai_fn(global)]
pub fn increment(num: &mut i64) { pub fn increment(num: &mut i64) {
*num += 1; *num += 1;
} }
@ -159,10 +161,12 @@ service::increment(x);
x == 43; x == 43;
``` ```
`Engine::register_module` also exposes all _methods_ and _iterators_ from the module to the Any functions (usually _methods_) defined in the module with `#[rhai_fn(global)]`, as well as
_global_ namespace, so [getters/setters] and [indexers] for [custom types] work as expected. all _type iterators_, are automatically exposed to the _global_ namespace, so iteration,
[getters/setters] and [indexers] for [custom types] can work as expected.
Therefore, in the example able, `increment` works fine when called in method-call style: Therefore, in the example above, the `increment` method (defined with `#[rhai_fn(global)]`)
works fine when called in method-call style:
```rust ```rust
let x = 42; let x = 42;

View File

@ -60,17 +60,22 @@ engine.register_module("calc", module);
engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module
``` ```
`Engine::register_module` also exposes all _methods_ and _iterators_ from the module to the `Module::set_fn_namespace` can expose functions (usually _methods_) in the module
_global_ namespace, so [getters/setters] and [indexers] for [custom types] work as expected. to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] can work as expected.
Type iterators, because of their special nature, are always exposed to the _global_ namespace.
```rust ```rust
use rhai::{Engine, Module}; use rhai::{Engine, Module, FnNamespace};
let mut module = Module::new(); // new module let mut module = Module::new(); // new module
module.set_fn_1_mut("inc", // add new method let hash = module.set_fn_1_mut("inc", // add new method
|x: &mut i64| Ok(x+1) |x: &mut i64| Ok(x+1)
); );
// Expose 'inc' to the global namespace (default is 'Internal')
module.set_fn_namespace(hash, FnNamespace::Global);
// Load the module into the Engine as a sub-module named 'calc' // Load the module into the Engine as a sub-module named 'calc'
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_module("calc", module); engine.register_module("calc", module);

View File

@ -6,7 +6,7 @@ Modules
Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_. Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_.
Modules can be disabled via the [`no_module`] feature. Modules can be disabled via the [`no_module`] feature.
A module is of the type `Module` and holds a collection of functions, variables, iterators and sub-modules. A module is of the type `Module` and holds a collection of functions, variables, type iterators and sub-modules.
It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions
and variables defined by that script. and variables defined by that script.

View File

@ -17,7 +17,9 @@ use crate::stdlib::{
use crate::syntax::FnCustomSyntaxEval; use crate::syntax::FnCustomSyntaxEval;
use crate::token::Token; use crate::token::Token;
use crate::utils::StraightHasherBuilder; use crate::utils::StraightHasherBuilder;
use crate::{Dynamic, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT, NO_POS}; use crate::{
Dynamic, FnNamespace, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT, NO_POS,
};
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::FLOAT; use crate::FLOAT;
@ -28,7 +30,7 @@ use crate::Array;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::Map; use crate::Map;
/// A type representing the access mode of a scripted function. /// A type representing the access mode of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnAccess { pub enum FnAccess {
/// Public function. /// Public function.
@ -37,16 +39,6 @@ pub enum FnAccess {
Private, Private,
} }
impl fmt::Display for FnAccess {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Private => write!(f, "private"),
Self::Public => write!(f, "public"),
}
}
}
impl FnAccess { impl FnAccess {
/// Is this access mode private? /// Is this access mode private?
#[inline(always)] #[inline(always)]
@ -178,7 +170,7 @@ impl AST {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub fn clone_functions_only(&self) -> Self { pub fn clone_functions_only(&self) -> Self {
self.clone_functions_only_filtered(|_, _, _| true) self.clone_functions_only_filtered(|_, _, _, _, _| true)
} }
/// Clone the `AST`'s functions into a new `AST` based on a filter predicate. /// Clone the `AST`'s functions into a new `AST` based on a filter predicate.
/// No statements are cloned. /// No statements are cloned.
@ -188,7 +180,7 @@ impl AST {
#[inline(always)] #[inline(always)]
pub fn clone_functions_only_filtered( pub fn clone_functions_only_filtered(
&self, &self,
mut filter: impl FnMut(FnAccess, &str, usize) -> bool, mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self { ) -> Self {
let mut functions: Module = Default::default(); let mut functions: Module = Default::default();
functions.merge_filtered(&self.1, &mut filter); functions.merge_filtered(&self.1, &mut filter);
@ -251,7 +243,7 @@ impl AST {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn merge(&self, other: &Self) -> Self { pub fn merge(&self, other: &Self) -> Self {
self.merge_filtered(other, |_, _, _| true) self.merge_filtered(other, |_, _, _, _, _| true)
} }
/// Combine one `AST` with another. The second `AST` is consumed. /// Combine one `AST` with another. The second `AST` is consumed.
/// ///
@ -303,7 +295,7 @@ impl AST {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn combine(&mut self, other: Self) -> &mut Self { pub fn combine(&mut self, other: Self) -> &mut Self {
self.combine_filtered(other, |_, _, _| true) self.combine_filtered(other, |_, _, _, _, _| true)
} }
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version /// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
/// is returned. /// is returned.
@ -339,7 +331,8 @@ impl AST {
/// "#)?; /// "#)?;
/// ///
/// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' /// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1'
/// let ast = ast1.merge_filtered(&ast2, |_, name, params| name == "error" && params == 0); /// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params|
/// script && name == "error" && params == 0);
/// ///
/// // 'ast' is essentially: /// // 'ast' is essentially:
/// // /// //
@ -360,7 +353,7 @@ impl AST {
pub fn merge_filtered( pub fn merge_filtered(
&self, &self,
other: &Self, other: &Self,
mut filter: impl FnMut(FnAccess, &str, usize) -> bool, mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self { ) -> Self {
let Self(statements, functions) = self; let Self(statements, functions) = self;
@ -413,7 +406,8 @@ impl AST {
/// "#)?; /// "#)?;
/// ///
/// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' /// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1'
/// ast1.combine_filtered(ast2, |_, name, params| name == "error" && params == 0); /// ast1.combine_filtered(ast2, |_, _, script, name, params|
/// script && name == "error" && params == 0);
/// ///
/// // 'ast1' is essentially: /// // 'ast1' is essentially:
/// // /// //
@ -434,7 +428,7 @@ impl AST {
pub fn combine_filtered( pub fn combine_filtered(
&mut self, &mut self,
other: Self, other: Self,
mut filter: impl FnMut(FnAccess, &str, usize) -> bool, mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> &mut Self { ) -> &mut Self {
let Self(ref mut statements, ref mut functions) = self; let Self(ref mut statements, ref mut functions) = self;
statements.extend(other.0.into_iter()); statements.extend(other.0.into_iter());
@ -459,22 +453,25 @@ impl AST {
/// "#)?; /// "#)?;
/// ///
/// // Remove all functions except 'foo(_)' /// // Remove all functions except 'foo(_)'
/// ast.retain_functions(|_, name, params| name == "foo" && params == 1); /// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1);
/// # } /// # }
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub fn retain_functions(&mut self, filter: impl FnMut(FnAccess, &str, usize) -> bool) { pub fn retain_functions(
self.1.retain_functions(filter); &mut self,
filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool,
) {
self.1.retain_script_functions(filter);
} }
/// Iterate through all functions /// Iterate through all functions
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub fn iter_functions<'a>( pub fn iter_functions<'a>(
&'a self, &'a self,
) -> impl Iterator<Item = (FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a { ) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a {
self.1.iter_script_fn() self.1.iter_script_fn()
} }
/// Clear all function definitions in the `AST`. /// Clear all function definitions in the `AST`.

View File

@ -52,7 +52,7 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
// We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond // We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
// the AST of the eval script text. The best we can do is a shared reference. // the AST of the eval script text. The best we can do is a shared reference.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Imports(StaticVec<(ImmutableString, bool, Shared<Module>)>); pub struct Imports(StaticVec<(ImmutableString, Shared<Module>)>);
impl Imports { impl Imports {
/// Get the length of this stack of imported modules. /// Get the length of this stack of imported modules.
@ -65,7 +65,7 @@ impl Imports {
} }
/// Get the imported module at a particular index. /// Get the imported module at a particular index.
pub fn get(&self, index: usize) -> Option<Shared<Module>> { pub fn get(&self, index: usize) -> Option<Shared<Module>> {
self.0.get(index).map(|(_, _, m)| m).cloned() self.0.get(index).map(|(_, m)| m).cloned()
} }
/// Get the index of an imported module by name. /// Get the index of an imported module by name.
pub fn find(&self, name: &str) -> Option<usize> { pub fn find(&self, name: &str) -> Option<usize> {
@ -73,21 +73,12 @@ impl Imports {
.iter() .iter()
.enumerate() .enumerate()
.rev() .rev()
.find(|(_, (key, _, _))| key.as_str() == name) .find(|(_, (key, _))| key.as_str() == name)
.map(|(index, _)| index) .map(|(index, _)| index)
} }
/// Push an imported module onto the stack. /// Push an imported module onto the stack.
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) { pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) {
self.0.push((name.into(), false, module.into())); self.0.push((name.into(), module.into()));
}
/// Push a fixed module onto the stack.
#[cfg(not(feature = "no_module"))]
pub(crate) fn push_fixed(
&mut self,
name: impl Into<ImmutableString>,
module: impl Into<Shared<Module>>,
) {
self.0.push((name.into(), true, module.into()));
} }
/// Truncate the stack of imported modules to a particular length. /// Truncate the stack of imported modules to a particular length.
pub fn truncate(&mut self, size: usize) { pub fn truncate(&mut self, size: usize) {
@ -95,58 +86,49 @@ impl Imports {
} }
/// Get an iterator to this stack of imported modules. /// Get an iterator to this stack of imported modules.
#[allow(dead_code)] #[allow(dead_code)]
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Shared<Module>)> { pub fn iter(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
self.0 self.0
.iter() .iter()
.map(|(name, fixed, module)| (name.as_str(), *fixed, module.clone())) .map(|(name, module)| (name.as_str(), module.clone()))
} }
/// Get an iterator to this stack of imported modules. /// Get an iterator to this stack of imported modules.
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn iter_raw<'a>( pub(crate) fn iter_raw<'a>(
&'a self, &'a self,
) -> impl Iterator<Item = (ImmutableString, bool, Shared<Module>)> + 'a { ) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> + 'a {
self.0.iter().cloned() self.0.iter().cloned()
} }
/// Get a consuming iterator to this stack of imported modules. /// Get a consuming iterator to this stack of imported modules.
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, bool, Shared<Module>)> { pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
self.0.into_iter() self.0.into_iter()
} }
/// Add a stream of imported modules. /// Add a stream of imported modules.
pub fn extend( pub fn extend(&mut self, stream: impl Iterator<Item = (ImmutableString, Shared<Module>)>) {
&mut self,
stream: impl Iterator<Item = (ImmutableString, bool, Shared<Module>)>,
) {
self.0.extend(stream) self.0.extend(stream)
} }
/// Does the specified function hash key exist in this stack of imported modules? /// Does the specified function hash key exist in this stack of imported modules?
#[allow(dead_code)] #[allow(dead_code)]
pub fn contains_fn(&self, hash: u64) -> bool { pub fn contains_fn(&self, hash: u64) -> bool {
self.0 self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash))
.iter()
.any(|(_, fixed, m)| *fixed && 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: u64) -> Option<&CallableFunction> {
self.0 self.0
.iter() .iter()
.rev() .rev()
.filter(|&&(_, fixed, _)| fixed) .find_map(|(_, m)| m.get_qualified_fn(hash))
.find_map(|(_, _, m)| m.get_qualified_fn(hash))
} }
/// Does the specified TypeId iterator exist in this stack of imported modules? /// Does the specified TypeId iterator exist in this stack of imported modules?
#[allow(dead_code)] #[allow(dead_code)]
pub fn contains_iter(&self, id: TypeId) -> bool { pub fn contains_iter(&self, id: TypeId) -> bool {
self.0 self.0.iter().any(|(_, m)| m.contains_qualified_iter(id))
.iter()
.any(|(_, fixed, m)| *fixed && m.contains_qualified_iter(id))
} }
/// Get the specified TypeId iterator. /// Get the specified TypeId iterator.
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> { pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.0 self.0
.iter() .iter()
.rev() .rev()
.filter(|&&(_, fixed, _)| fixed) .find_map(|(_, m)| m.get_qualified_iter(id))
.find_map(|(_, _, m)| m.get_qualified_iter(id))
} }
} }

View File

@ -12,7 +12,8 @@ use crate::stdlib::{
}; };
use crate::utils::get_hasher; use crate::utils::get_hasher;
use crate::{ use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, NativeCallContext, ParseError, AST, NO_POS, scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, NativeCallContext,
ParseError, AST, NO_POS,
}; };
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -55,7 +56,8 @@ impl Engine {
+ SendSync + SendSync
+ 'static, + 'static,
) -> &mut Self { ) -> &mut Self {
self.global_module.set_raw_fn(name, arg_types, func); self.global_module
.set_raw_fn(name, FnNamespace::Global, FnAccess::Public, arg_types, func);
self self
} }
/// Register a custom type for use with the `Engine`. /// Register a custom type for use with the `Engine`.
@ -751,9 +753,9 @@ impl Engine {
// Index the module (making a clone copy if necessary) if it is not indexed // Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module); let mut module = crate::fn_native::shared_take_or_clone(module);
module.build_index(); module.build_index();
self.global_sub_modules.push_fixed(name, module); self.global_sub_modules.push(name, module);
} else { } else {
self.global_sub_modules.push_fixed(name, module); self.global_sub_modules.push(name, module);
} }
self self
} }

View File

@ -2,12 +2,13 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::ast::FnAccess;
use crate::dynamic::{DynamicWriteLock, Variant}; use crate::dynamic::{DynamicWriteLock, Variant};
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
use crate::r#unsafe::unsafe_cast_box; use crate::r#unsafe::unsafe_cast_box;
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String}; use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
use crate::{Dynamic, Engine, EvalAltResult, ImmutableString, NativeCallContext}; use crate::{
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, NativeCallContext,
};
/// Trait to register custom functions with the `Engine`. /// Trait to register custom functions with the `Engine`.
pub trait RegisterFn<FN, ARGS, RET> { pub trait RegisterFn<FN, ARGS, RET> {
@ -186,7 +187,7 @@ macro_rules! def_register {
{ {
#[inline] #[inline]
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self { fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
self.global_module.set_fn(name, FnAccess::Public, self.global_module.set_fn(name, FnNamespace::Global, FnAccess::Public,
&[$(map_type_id::<$par>()),*], &[$(map_type_id::<$par>()),*],
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*)) CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*))
); );
@ -201,7 +202,7 @@ macro_rules! def_register {
{ {
#[inline] #[inline]
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self { fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
self.global_module.set_fn(name, FnAccess::Public, self.global_module.set_fn(name, FnNamespace::Global, FnAccess::Public,
&[$(map_type_id::<$par>()),*], &[$(map_type_id::<$par>()),*],
CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*)) CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*))
); );

View File

@ -111,12 +111,12 @@ pub type FLOAT = f64;
#[cfg(feature = "f32_float")] #[cfg(feature = "f32_float")]
pub type FLOAT = f32; pub type FLOAT = f32;
pub use ast::AST; pub use ast::{FnAccess, AST};
pub use dynamic::Dynamic; pub use dynamic::Dynamic;
pub use engine::{Engine, EvalContext}; pub use engine::{Engine, EvalContext};
pub use fn_native::{FnPtr, NativeCallContext}; pub use fn_native::{FnPtr, NativeCallContext};
pub use fn_register::{RegisterFn, RegisterResultFn}; pub use fn_register::{RegisterFn, RegisterResultFn};
pub use module::Module; pub use module::{FnNamespace, Module};
pub use parse_error::{LexError, ParseError, ParseErrorType}; pub use parse_error::{LexError, ParseError, ParseErrorType};
pub use result::EvalAltResult; pub use result::EvalAltResult;
pub use scope::Scope; pub use scope::Scope;
@ -135,9 +135,6 @@ pub(crate) use utils::{calc_native_fn_hash, calc_script_fn_hash};
pub use rhai_codegen::*; pub use rhai_codegen::*;
#[cfg(not(feature = "no_function"))]
pub use ast::FnAccess;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use fn_func::Func; pub use fn_func::Func;

View File

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

View File

@ -25,8 +25,8 @@ use crate::syntax::CustomSyntax;
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream}; use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
use crate::utils::{get_hasher, StraightHasherBuilder}; use crate::utils::{get_hasher, StraightHasherBuilder};
use crate::{ use crate::{
calc_script_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, calc_script_fn_hash, Dynamic, Engine, FnAccess, ImmutableString, LexError, ParseError,
Position, Scope, StaticVec, AST, NO_POS, ParseErrorType, Position, Scope, StaticVec, AST, NO_POS,
}; };
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -2371,9 +2371,9 @@ fn parse_stmt(
Token::Fn | Token::Private => { Token::Fn | Token::Private => {
let access = if matches!(token, Token::Private) { let access = if matches!(token, Token::Private) {
eat_token(input, Token::Private); eat_token(input, Token::Private);
crate::ast::FnAccess::Private FnAccess::Private
} else { } else {
crate::ast::FnAccess::Public FnAccess::Public
}; };
match input.next().unwrap() { match input.next().unwrap() {
@ -2554,7 +2554,7 @@ fn parse_fn(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
access: crate::ast::FnAccess, access: FnAccess,
mut settings: ParseSettings, mut settings: ParseSettings,
) -> Result<ScriptFnDef, ParseError> { ) -> Result<ScriptFnDef, ParseError> {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -2789,7 +2789,7 @@ fn parse_anon_fn(
// Define the function // Define the function
let script = ScriptFnDef { let script = ScriptFnDef {
name: fn_name.clone(), name: fn_name.clone(),
access: crate::ast::FnAccess::Public, access: FnAccess::Public,
params, params,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
externals: Default::default(), externals: Default::default(),

View File

@ -1,11 +1,10 @@
//! Module defining macros for developing _plugins_. //! Module defining macros for developing _plugins_.
pub use crate::ast::FnAccess;
pub use crate::fn_native::{CallableFunction, FnCallArgs}; pub use crate::fn_native::{CallableFunction, FnCallArgs};
pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec}; pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec};
pub use crate::{ pub use crate::{
Dynamic, Engine, EvalAltResult, ImmutableString, Module, NativeCallContext, RegisterFn, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
RegisterResultFn, NativeCallContext, RegisterFn, RegisterResultFn,
}; };
#[cfg(not(features = "no_module"))] #[cfg(not(features = "no_module"))]

View File

@ -105,15 +105,17 @@ pub fn get_hasher() -> impl Hasher {
/// ///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
fn calc_fn_hash<'a>( fn calc_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>, mut modules: impl Iterator<Item = &'a str>,
fn_name: &str, fn_name: &str,
num: Option<usize>, num: Option<usize>,
params: impl Iterator<Item = TypeId>, params: impl Iterator<Item = TypeId>,
) -> u64 { ) -> u64 {
let s = &mut get_hasher(); let s = &mut get_hasher();
// Hash a boolean indicating whether the hash is namespace-qualified.
modules.next().is_some().hash(s);
// We always skip the first module // We always skip the first module
modules.skip(1).for_each(|m| m.hash(s)); modules.for_each(|m| m.hash(s));
s.write(fn_name.as_bytes()); s.write(fn_name.as_bytes());
if let Some(num) = num { if let Some(num) = num {
s.write_usize(num); s.write_usize(num);

View File

@ -267,7 +267,7 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
let fn_ptr = engine.eval_ast::<FnPtr>(&ast)?; let fn_ptr = engine.eval_ast::<FnPtr>(&ast)?;
// Get rid of the script, retaining only functions // Get rid of the script, retaining only functions
ast.retain_functions(|_, _, _| true); ast.retain_functions(|_, _, _, _| true);
// Closure 'f' captures: the engine, the AST, and the curried function pointer // Closure 'f' captures: the engine, the AST, and the curried function pointer
let f = move |x: INT| fn_ptr.call_dynamic((&engine, &[ast.as_ref()]).into(), None, [x.into()]); let f = move |x: INT| fn_ptr.call_dynamic((&engine, &[ast.as_ref()]).into(), None, [x.into()]);

View File

@ -1,7 +1,7 @@
#![cfg(not(feature = "no_module"))] #![cfg(not(feature = "no_module"))]
use rhai::{ use rhai::{
module_resolvers::StaticModuleResolver, Dynamic, Engine, EvalAltResult, ImmutableString, module_resolvers::StaticModuleResolver, Dynamic, Engine, EvalAltResult, FnNamespace,
Module, ParseError, ParseErrorType, Scope, INT, ImmutableString, Module, ParseError, ParseErrorType, Scope, INT,
}; };
#[test] #[test]
@ -23,7 +23,8 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
sub_module2.set_var("answer", 41 as INT); sub_module2.set_var("answer", 41 as INT);
let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1)); let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1)); let hash_super_inc = sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1));
sub_module2.set_fn_namespace(hash_super_inc, FnNamespace::Global);
sub_module.set_sub_module("universe", sub_module2); sub_module.set_sub_module("universe", sub_module2);
module.set_sub_module("life", sub_module); module.set_sub_module("life", sub_module);