Skip to content

Commit

Permalink
Merge pull request #945 from schungx/master
Browse files Browse the repository at this point in the history
Add collect_fn_metadata.
  • Loading branch information
schungx authored Dec 31, 2024
2 parents 2ed0d0e + f2eeb94 commit ae662cb
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 176 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
Rhai Release Notes
==================

Version 1.21.0
Version 1.20.1
==============

Bug fixes
---------

* Fixed bug in raw strings with newlines (thanks [`@benatkin`](https://github.com/benatkin) [940](https://github.com/rhaiscript/rhai/pull/940)).
* `get_fn_metadata_list` function is marked `volatile`.

Enhancements
------------

* If a string slice refers to the entire string, the slice is not cloned but returned as-is.
* A new `internals` function, `Engine::collect_fn_metadata`, is added to collect all functions metadata. This is to facilitate better error reporting for missing functions (thanks [`therealprof`](https://github.com/therealprof) [899](https://github.com/rhaiscript/rhai/issues/899)).


Version 1.20.0
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = [".", "codegen"]

[package]
name = "rhai"
version = "1.21.0"
version = "1.20.1"
rust-version = "1.66.0"
edition = "2018"
resolver = "2"
Expand Down
130 changes: 130 additions & 0 deletions src/api/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,4 +752,134 @@ impl Engine {

signatures
}

/// Collect the [`FuncInfo`][crate::module::FuncInfo] of all functions, native or script-defined,
/// mapping them into any type.
/// Exported under the `internals` feature only.
///
/// Return [`None`] from the `mapper` to skip a function.
///
/// Functions from the following sources are included, in order:
/// 1) Functions defined in the current script (if any)
/// 2) Functions registered into the global namespace
/// 3) Functions in registered packages
/// 4) Functions in standard packages (optional)
/// 5) Functions defined in modules `import`-ed by the current script (if any)
/// 6) Functions in registered sub-modules
#[cfg(feature = "internals")]
#[inline(always)]
pub fn collect_fn_metadata<T>(
&self,
ctx: Option<&NativeCallContext>,
mapper: impl Fn(crate::module::FuncInfo) -> Option<T> + Copy,
include_standard_packages: bool,
) -> Vec<T> {
self.collect_fn_metadata_impl(ctx, mapper, include_standard_packages)
}

/// Collect the [`FuncInfo`][crate::module::FuncInfo] of all functions, native or script-defined,
/// mapping them into any type.
///
/// Return [`None`] from the `mapper` to skip a function.
///
/// Functions from the following sources are included, in order:
/// 1) Functions defined in the current script (if any)
/// 2) Functions registered into the global namespace
/// 3) Functions in registered packages
/// 4) Functions in standard packages (optional)
/// 5) Functions defined in modules `import`-ed by the current script (if any)
/// 6) Functions in registered sub-modules
#[allow(dead_code)]
pub(crate) fn collect_fn_metadata_impl<T>(
&self,
ctx: Option<&NativeCallContext>,

Check warning on line 795 in src/api/register.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_time,no_function,no_float,no_position,no_inde...

unused variable: `ctx`

Check warning on line 795 in src/api/register.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,sync,no_time,no_function,no_float,no_position,no...

unused variable: `ctx`
mapper: impl Fn(crate::module::FuncInfo) -> Option<T> + Copy,
include_standard_packages: bool,
) -> Vec<T> {
let mut list = Vec::new();

#[cfg(not(feature = "no_function"))]
if let Some(ctx) = ctx {
ctx.iter_namespaces()
.flat_map(Module::iter_fn)
.filter_map(|(func, f)| {
mapper(crate::module::FuncInfo {
metadata: f,
#[cfg(not(feature = "no_module"))]
namespace: Identifier::new_const(),
script: func.get_script_fn_def().map(|f| (&**f).into()),
})
})
.for_each(|v| list.push(v));
}

self.global_modules
.iter()
.filter(|m| !m.is_internal() && (include_standard_packages || !m.is_standard_lib()))
.flat_map(|m| m.iter_fn())
.filter_map(|(_func, f)| {
mapper(crate::module::FuncInfo {
metadata: f,
#[cfg(not(feature = "no_module"))]
namespace: Identifier::new_const(),
#[cfg(not(feature = "no_function"))]
script: _func.get_script_fn_def().map(|f| (&**f).into()),
})
})
.for_each(|v| list.push(v));

#[cfg(not(feature = "no_module"))]
if let Some(ctx) = ctx {
use crate::engine::NAMESPACE_SEPARATOR;
use crate::SmartString;

// Recursively scan modules for script-defined functions.
fn scan_module<T>(
list: &mut Vec<T>,
namespace: &str,
module: &Module,
mapper: impl Fn(crate::module::FuncInfo) -> Option<T> + Copy,
) {
module
.iter_fn()
.filter_map(|(_func, f)| {
mapper(crate::module::FuncInfo {
metadata: f,
namespace: namespace.into(),
#[cfg(not(feature = "no_function"))]
script: _func.get_script_fn_def().map(|f| (&**f).into()),
})
})
.for_each(|v| list.push(v));

for (name, m) in module.iter_sub_modules() {
use std::fmt::Write;

let mut ns = SmartString::new_const();
write!(&mut ns, "{namespace}{NAMESPACE_SEPARATOR}{name}").unwrap();
scan_module(list, &ns, m, mapper);
}
}

for (ns, m) in ctx.global_runtime_state().iter_imports_raw() {
scan_module(&mut list, ns, m, mapper);
}
}

#[cfg(not(feature = "no_module"))]
self.global_sub_modules
.values()
.flat_map(|m| m.iter_fn())
.filter_map(|(_func, f)| {
mapper(crate::module::FuncInfo {
metadata: f,
namespace: Identifier::new_const(),
#[cfg(not(feature = "no_function"))]
script: _func.get_script_fn_def().map(|f| (&**f).into()),
})
})
.for_each(|v| list.push(v));

list
}
}
7 changes: 3 additions & 4 deletions src/eval/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@ use crate::func::{get_builtin_op_assignment_fn, get_hasher};
use crate::tokenizer::Token;
use crate::types::dynamic::{AccessMode, Union};
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, VarDefInfo, ERR, INT};
use std::hash::{Hash, Hasher};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
convert::TryInto,
hash::{Hash, Hasher},
};

impl Engine {
/// If the value is a string, intern it.
Expand Down Expand Up @@ -307,6 +304,8 @@ impl Engine {

#[cfg(not(feature = "no_function"))]
{
use std::convert::TryInto;

let rhs_val = self
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
.flatten();
Expand Down
6 changes: 4 additions & 2 deletions src/eval/target.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Type to hold a mutable reference to the target of an evaluation.
use crate::{Dynamic, EvalAltResult, Position, RhaiError, RhaiResultOf};
use crate::{Dynamic, Position, RhaiError, RhaiResultOf};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
Expand Down Expand Up @@ -428,7 +428,9 @@ impl<'a> TryFrom<&'a mut Dynamic> for Target<'a> {
// Cloning is cheap for a shared value
let shared_value = value.clone();
let Some(guard) = value.write_lock::<Dynamic>() else {
return Err(EvalAltResult::ErrorDataRace(String::new(), Position::NONE).into());
return Err(
crate::EvalAltResult::ErrorDataRace(String::new(), Position::NONE).into(),
);
};
return Ok(Self::SharedValue {
guard,
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ pub use optimizer::OptimizationLevel;
#[cfg(feature = "internals")]
pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant};

#[cfg(feature = "internals")]
pub use module::{FuncInfo, FuncMetadata};

#[cfg(feature = "internals")]
#[cfg(not(feature = "no_float"))]
pub use types::FloatWrapper;
Expand Down
27 changes: 25 additions & 2 deletions src/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ impl FnNamespace {
}
}

/// A type containing the metadata of a single registered function.
/// _(internals)_ A type containing the metadata of a single registered function.
/// Exported under the `internals` features only.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub struct FuncMetadata {
Expand All @@ -86,12 +87,15 @@ pub struct FuncMetadata {
/// Parameter types (if applicable).
pub param_types: FnArgsVec<TypeId>,
/// Parameter names and types (if available).
/// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")]
pub params_info: FnArgsVec<Identifier>,
/// Return type name.
/// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")]
pub return_type: Identifier,
/// Comments.
/// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")]
pub comments: crate::StaticVec<SmartString>,
}
Expand Down Expand Up @@ -152,6 +156,25 @@ impl FuncMetadata {
}
}

/// Information about a function, native or scripted.
///
/// Exported under the `internals` feature only.
#[allow(dead_code)]
pub struct FuncInfo<'a> {
/// Function metadata.
pub metadata: &'a FuncMetadata,
/// Function namespace.
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
pub namespace: Identifier,
/// Metadata if the function is scripted.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub script: Option<crate::ScriptFnMetadata<'a>>,
}

/// _(internals)_ Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types.
/// Exported under the `internals` feature only.
///
Expand Down Expand Up @@ -400,7 +423,7 @@ impl FuncRegistration {
{
#[cfg(feature = "metadata")]
{
// Do not update parameter informations if `with_params_info` was called previously.
// Do not update parameter information if `with_params_info` was called previously.
if self.metadata.params_info.is_empty() {
let mut param_type_names = FUNC::param_names()
.iter()
Expand Down
Loading

0 comments on commit ae662cb

Please sign in to comment.