diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06631443..cfca586f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: os: [ubuntu-latest] flags: - "" - - "--features metadata,internals" + - "--features metadata,serde,internals" - "--features unchecked" - "--features sync" - "--features no_optimize" @@ -34,7 +34,7 @@ jobs: - "--features no_module" - "--features no_closure" - "--features unicode-xid-ident" - - "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,unchecked" + - "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,serde,unchecked" toolchain: [stable] experimental: [false] include: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0efd5778..389748c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Breaking changes * `Array::reduce` and `Array::reduce_rev` now take a `Dynamic` as initial value instead of a function pointer. * `protected`, `super` are now reserved keywords. * The `Module::set_fn_XXX` API now take `&str` as the function name instead of `Into`. +* The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate. Enhancements ------------ diff --git a/Cargo.toml b/Cargo.toml index 849e2e1e..b7ecc4f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ no_closure = [] # no automatic sharing and capture of anonymous no_module = [] # no modules internals = [] # expose internal data structures unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. -metadata = ["serde", "serde_json"] # enables exporting functions metadata to JSON +metadata = ["serde_json"] # enable exporting functions metadata no_std = ["smallvec/union", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"] @@ -92,4 +92,4 @@ instant = { version = "0.1" } # WASM implementation of std::time::Instant instant = { version = "0.1" } # WASM implementation of std::time::Instant [package.metadata.docs.rs] -features = ["metadata", "internals", "decimal"] # compiling for no-std +features = ["metadata", "serde", "internals", "decimal"] # compiling for no-std diff --git a/src/ast.rs b/src/ast.rs index 4c04160f..51da78e6 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -84,6 +84,7 @@ impl fmt::Display for ScriptFnDef { } /// A type containing the metadata of a script-defined function. +/// Not available under `no_function`. /// /// Created by [`AST::iter_functions`]. #[cfg(not(feature = "no_function"))] @@ -260,6 +261,7 @@ impl AST { } /// _(INTERNALS)_ Get the internal shared [`Module`] containing all script-defined functions. /// Exported under the `internals` feature only. + /// Not available under `no_function`. #[cfg(feature = "internals")] #[deprecated = "this method is volatile and may change"] #[cfg(not(feature = "no_module"))] @@ -276,6 +278,7 @@ impl AST { } /// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions. /// Exported under the `internals` feature only. + /// Not available under `no_function`. #[cfg(feature = "internals")] #[deprecated = "this method is volatile and may change"] #[inline(always)] @@ -311,10 +314,9 @@ impl AST { } /// Clone the [`AST`]'s functions into a new [`AST`]. /// No statements are cloned. + /// Not available under `no_function`. /// /// This operation is cheap because functions are shared. - /// - /// Not available under `no_function`. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn clone_functions_only(&self) -> Self { @@ -322,10 +324,9 @@ impl AST { } /// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate. /// No statements are cloned. + /// Not available under `no_function`. /// /// This operation is cheap because functions are shared. - /// - /// Not available under `no_function`. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn clone_functions_only_filtered( @@ -354,8 +355,8 @@ impl AST { resolver: self.resolver.clone(), } } - /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version - /// is returned. + /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, + /// version is returned. /// /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 291842e2..5dc2fd75 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -45,6 +45,7 @@ fn print_help() { println!("help => print this help"); println!("quit, exit => quit"); println!("scope => print all variables in the scope"); + #[cfg(feature = "metadata")] println!("functions => print all functions defined"); println!("ast => print the last AST (optimized)"); println!("astu => print the last raw, un-optimized AST"); @@ -202,6 +203,7 @@ fn main() { println!("{:#?}\n", ast); continue; } + #[cfg(feature = "metadata")] "functions" => { // print a list of all registered functions engine diff --git a/src/engine_api.rs b/src/engine_api.rs index c13bbb0d..96abbbbf 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -9,12 +9,11 @@ use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, format, - string::{String, ToString}, - vec::Vec, + string::String, }; use crate::{ scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module, - NativeCallContext, ParseError, Position, RhaiResult, Shared, StaticVec, AST, + NativeCallContext, ParseError, Position, RhaiResult, Shared, AST, }; #[cfg(not(feature = "no_index"))] @@ -52,30 +51,36 @@ impl Engine { /// # } /// ``` #[inline] - pub fn register_fn( - &mut self, - name: impl AsRef + Into, - func: F, - ) -> &mut Self + pub fn register_fn(&mut self, name: N, func: F) -> &mut Self where + N: AsRef + Into, F: RegisterNativeFunction, { let param_types = F::param_types(); - let mut param_type_names: StaticVec<_> = F::param_names() + + #[cfg(feature = "metadata")] + let mut param_type_names: crate::StaticVec<_> = F::param_names() .iter() .map(|ty| format!("_: {}", self.map_type_name(ty))) .collect(); + + #[cfg(feature = "metadata")] if F::return_type() != TypeId::of::<()>() { - param_type_names.push(self.map_type_name(F::return_type_name()).to_string()); + param_type_names.push(self.map_type_name(F::return_type_name()).into()); } - let param_type_names: StaticVec<_> = - param_type_names.iter().map(|ty| ty.as_str()).collect(); + + #[cfg(feature = "metadata")] + let param_type_names: Option> = + Some(param_type_names.iter().map(|ty| ty.as_str()).collect()); + + #[cfg(not(feature = "metadata"))] + let param_type_names: Option<[&str; 0]> = None; self.global_namespace.set_fn( name, FnNamespace::Global, FnAccess::Public, - Some(¶m_type_names), + param_type_names.as_ref().map(|v| v.as_ref()), ¶m_types, func.into_callable_function(), ); @@ -106,28 +111,34 @@ impl Engine { /// .expect_err("expecting division by zero error!"); /// ``` #[inline] - pub fn register_result_fn( - &mut self, - name: impl AsRef + Into, - func: F, - ) -> &mut Self + pub fn register_result_fn(&mut self, name: N, func: F) -> &mut Self where + N: AsRef + Into, F: RegisterNativeFunction>>, { let param_types = F::param_types(); - let mut param_type_names: StaticVec<_> = F::param_names() + + #[cfg(feature = "metadata")] + let param_type_names: crate::StaticVec<_> = F::param_names() .iter() .map(|ty| format!("_: {}", self.map_type_name(ty))) + .chain(crate::stdlib::iter::once( + self.map_type_name(F::return_type_name()).into(), + )) .collect(); - param_type_names.push(self.map_type_name(F::return_type_name()).to_string()); - let param_type_names: StaticVec<&str> = - param_type_names.iter().map(|ty| ty.as_str()).collect(); + + #[cfg(feature = "metadata")] + let param_type_names: Option> = + Some(param_type_names.iter().map(|ty| ty.as_str()).collect()); + + #[cfg(not(feature = "metadata"))] + let param_type_names: Option<[&str; 0]> = None; self.global_namespace.set_fn( name, FnNamespace::Global, FnAccess::Public, - Some(¶m_type_names), + param_type_names.as_ref().map(|v| v.as_ref()), ¶m_types, func.into_callable_function(), ); @@ -150,14 +161,18 @@ impl Engine { /// To access the first mutable parameter, use `args.get_mut(0).unwrap()` #[deprecated = "this function is volatile and may change"] #[inline(always)] - pub fn register_raw_fn( + pub fn register_raw_fn( &mut self, - name: impl AsRef + Into, + name: N, arg_types: &[TypeId], func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result> + SendSync + 'static, - ) -> &mut Self { + ) -> &mut Self + where + N: AsRef + Into, + T: Variant + Clone, + { self.global_namespace.set_raw_fn( name, FnNamespace::Global, @@ -1958,13 +1973,15 @@ impl Engine { crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level) } /// Generate a list of all registered functions. + /// Available under the `metadata` feature only. /// /// Functions from the following sources are included, in order: /// 1) Functions registered into the global namespace /// 2) Functions in registered sub-modules /// 3) Functions in packages (optional) - pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec { - let mut signatures: Vec<_> = Default::default(); + #[cfg(feature = "metadata")] + pub fn gen_fn_signatures(&self, include_packages: bool) -> crate::stdlib::vec::Vec { + let mut signatures: crate::stdlib::vec::Vec<_> = Default::default(); signatures.extend(self.global_namespace.gen_fn_signatures()); diff --git a/src/fn_register.rs b/src/fn_register.rs index b4047a72..134e55ad 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -5,13 +5,7 @@ use crate::dynamic::{DynamicWriteLock, Variant}; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::r#unsafe::unsafe_try_cast; -use crate::stdlib::{ - any::{type_name, TypeId}, - boxed::Box, - mem, - string::String, - vec, -}; +use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String, vec}; use crate::{Dynamic, EvalAltResult, NativeCallContext}; // These types are used to build a unique _marker_ tuple type for each combination @@ -66,10 +60,16 @@ pub trait RegisterNativeFunction { /// Get the type ID's of this function's parameters. fn param_types() -> Box<[TypeId]>; /// Get the type names of this function's parameters. + /// Available under the `metadata` feature only. + #[cfg(feature = "metadata")] fn param_names() -> Box<[&'static str]>; /// Get the type ID of this function's return value. + /// Available under the `metadata` feature only. + #[cfg(feature = "metadata")] fn return_type() -> TypeId; /// Get the type name of this function's return value. + /// Available under the `metadata` feature only. + #[cfg(feature = "metadata")] fn return_type_name() -> &'static str; } @@ -91,9 +91,9 @@ macro_rules! def_register { RET: Variant + Clone > RegisterNativeFunction<($($mark,)*), ()> for FN { #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } - #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() } - #[inline(always)] fn return_type() -> TypeId { TypeId::of::() } - #[inline(always)] fn return_type_name() -> &'static str { type_name::() } + #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! @@ -115,9 +115,9 @@ macro_rules! def_register { RET: Variant + Clone > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN { #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } - #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() } - #[inline(always)] fn return_type() -> TypeId { TypeId::of::() } - #[inline(always)] fn return_type_name() -> &'static str { type_name::() } + #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! @@ -139,9 +139,9 @@ macro_rules! def_register { RET: Variant + Clone > RegisterNativeFunction<($($mark,)*), Result>> for FN { #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } - #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() } - #[inline(always)] fn return_type() -> TypeId { TypeId::of::>>() } - #[inline(always)] fn return_type_name() -> &'static str { type_name::>>() } + #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::>>() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::>>() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! @@ -160,9 +160,9 @@ macro_rules! def_register { RET: Variant + Clone > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), Result>> for FN { #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } - #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() } - #[inline(always)] fn return_type() -> TypeId { TypeId::of::>>() } - #[inline(always)] fn return_type_name() -> &'static str { type_name::>>() } + #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::>>() } + #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::>>() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! diff --git a/src/module/mod.rs b/src/module/mod.rs index 770a9fe7..e0d9a60b 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -12,7 +12,7 @@ use crate::stdlib::{ iter::empty, num::NonZeroUsize, ops::{Add, AddAssign, Deref, DerefMut}, - string::{String, ToString}, + string::String, vec::Vec, }; use crate::token::Token; @@ -61,21 +61,21 @@ pub struct FuncInfo { /// Parameter types (if applicable). pub param_types: StaticVec, /// Parameter names (if available). + #[cfg(feature = "metadata")] pub param_names: StaticVec, } impl FuncInfo { /// Generate a signature of the function. + /// Available under the `metadata` feature only. + #[cfg(feature = "metadata")] pub fn gen_signature(&self) -> String { let mut sig = format!("{}(", self.name); if !self.param_names.is_empty() { - let mut params: Vec<_> = self - .param_names - .iter() - .map(ImmutableString::to_string) - .collect(); - let return_type = params.pop().unwrap_or_else(|| "()".to_string()); + let mut params: crate::stdlib::vec::Vec = + self.param_names.iter().map(|s| s.as_str().into()).collect(); + let return_type = params.pop().unwrap_or_else(|| "()".into()); sig.push_str(¶ms.join(", ")); if return_type != "()" { sig.push_str(") -> "); @@ -190,7 +190,7 @@ impl fmt::Debug for Module { .join(", ") ) } else { - "".to_string() + Default::default() }, if !self.variables.is_empty() { format!( @@ -202,19 +202,19 @@ impl fmt::Debug for Module { .join(", ") ) } else { - "".to_string() + Default::default() }, if !self.functions.is_empty() { format!( " functions: {}\n", self.functions .values() - .map(|f| f.func.to_string()) + .map(|f| crate::stdlib::string::ToString::to_string(&f.func)) .collect::>() .join(", ") ) } else { - "".to_string() + Default::default() } ) } @@ -363,6 +363,8 @@ impl Module { } /// Generate signatures for all the non-private functions in the [`Module`]. + /// Available under the `metadata` feature only. + #[cfg(feature = "metadata")] #[inline(always)] pub fn gen_fn_signatures(&self) -> impl Iterator + '_ { self.functions @@ -479,6 +481,7 @@ impl Module { access: fn_def.access, params: num_params, param_types: Default::default(), + #[cfg(feature = "metadata")] param_names, func: fn_def.into(), }), @@ -602,6 +605,7 @@ impl Module { } /// Update the metadata (parameter names/types and return type) of a registered function. + /// Available under the `metadata` feature only. /// /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. /// @@ -613,6 +617,7 @@ impl Module { /// /// The _last entry_ in the list should be the _return type_ of the function. /// In other words, the number of entries should be one larger than the number of parameters. + #[cfg(feature = "metadata")] #[inline(always)] pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self { let param_names = arg_names @@ -652,7 +657,7 @@ impl Module { name: impl AsRef + Into, namespace: FnNamespace, access: FnAccess, - arg_names: Option<&[&str]>, + _arg_names: Option<&[&str]>, arg_types: &[TypeId], func: CallableFunction, ) -> u64 { @@ -683,7 +688,9 @@ impl Module { let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types); let name = self.interned_strings.get(name); - let param_names = arg_names + + #[cfg(feature = "metadata")] + let param_names = _arg_names .iter() .flat_map(|p| p.iter()) .map(|&arg| self.interned_strings.get(arg)) @@ -697,6 +704,7 @@ impl Module { access, params: param_types.len(), param_types, + #[cfg(feature = "metadata")] param_names, func: func.into(), }), diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 5eae23c6..3872c45d 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -142,8 +142,10 @@ macro_rules! reg_range { ($lib:ident | $x:expr => $( $y:ty ),*) => { $( $lib.set_iterator::>(); - let hash = $lib.set_native_fn($x, get_range::<$y>); - $lib.update_fn_metadata(hash, &[ + let _hash = $lib.set_native_fn($x, get_range::<$y>); + + #[cfg(feature = "metadata")] + $lib.update_fn_metadata(_hash, &[ concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y)), concat!("Iterator") @@ -153,8 +155,10 @@ macro_rules! reg_range { ($lib:ident | step $x:expr => $( $y:ty ),*) => { $( $lib.set_iterator::>(); - let hash = $lib.set_native_fn($x, get_step_range::<$y>); - $lib.update_fn_metadata(hash, &[ + let _hash = $lib.set_native_fn($x, get_step_range::<$y>); + + #[cfg(feature = "metadata")] + $lib.update_fn_metadata(_hash, &[ concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y)), concat!("step: ", stringify!($y)), diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 3f8c9bed..dbb92ce3 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -211,7 +211,8 @@ impl From<&crate::Module> for ModuleMetadata { #[cfg(feature = "metadata")] impl Engine { /// _(METADATA)_ Generate a list of all functions (including those defined in an - /// [`AST`][crate::AST]) in JSON format. Available only under the `metadata` feature. + /// [`AST`][crate::AST]) in JSON format. + /// Available under the `metadata` feature only. /// /// Functions from the following sources are included: /// 1) Functions defined in an [`AST`][crate::AST] diff --git a/src/serde/mod.rs b/src/serde/mod.rs index a3819556..adb605de 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -8,6 +8,7 @@ mod serialize; mod str; #[cfg(feature = "metadata")] +#[cfg(feature = "serde")] mod metadata; pub use de::from_dynamic;