From 2f8a3c24c00d9074b60656d35aa841a97e521b5f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 14 Mar 2021 22:56:06 +0800 Subject: [PATCH 01/15] Bump version. --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d67f2585..b705550f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Rhai Release Notes ================== +Version 0.19.15 +=============== + + Version 0.19.14 =============== diff --git a/Cargo.toml b/Cargo.toml index f1759877..1719c147 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "codegen"] [package] name = "rhai" -version = "0.19.14" +version = "0.19.15" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" From d0922adb5b75b97b5a9ee0ea8c00ed537386293b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 15 Mar 2021 11:36:30 +0800 Subject: [PATCH 02/15] Deprecate RegisterFn and RegisterResultFn. --- CHANGELOG.md | 6 + benches/engine.rs | 2 +- benches/eval_type.rs | 2 +- codegen/src/lib.rs | 4 +- codegen/tests/test_functions.rs | 2 +- codegen/tests/test_modules.rs | 2 +- codegen/tests/test_nested.rs | 2 +- examples/arrays_and_structs.rs | 2 +- examples/custom_types_and_methods.rs | 2 +- examples/simple_fn.rs | 2 +- examples/strings.rs | 2 +- examples/threading.rs | 2 +- src/engine_api.rs | 112 +++++++++++---- src/engine_settings.rs | 2 +- src/fn_native.rs | 4 +- src/fn_register.rs | 195 +++++++++++---------------- src/lib.rs | 4 +- src/packages/array_basic.rs | 50 +++---- src/plugin.rs | 2 +- tests/arrays.rs | 2 +- tests/call_fn.rs | 4 +- tests/closures.rs | 10 +- tests/constants.rs | 2 +- tests/float.rs | 2 +- tests/fn_ptr.rs | 2 +- tests/functions.rs | 2 +- tests/get_set.rs | 2 +- tests/method_call.rs | 2 +- tests/mismatched_op.rs | 2 +- tests/optimizer.rs | 2 +- tests/print.rs | 2 +- tests/side_effects.rs | 2 +- tests/string.rs | 2 +- tests/tokens.rs | 2 +- tests/types.rs | 2 +- 35 files changed, 235 insertions(+), 206 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b705550f..2a841377 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ Rhai Release Notes Version 0.19.15 =============== +Breaking changes +---------------- + +* The traits `RegisterFn` and `RegisterResultFn` are removed. `Engine::register_fn` and `Engine::register_result_fn` are now implemented directly on `Engine`. +* `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it. + Version 0.19.14 =============== diff --git a/benches/engine.rs b/benches/engine.rs index 0cfc2972..70643a08 100644 --- a/benches/engine.rs +++ b/benches/engine.rs @@ -3,7 +3,7 @@ ///! Test evaluating expressions extern crate test; -use rhai::{Array, Engine, Map, RegisterFn, INT}; +use rhai::{Array, Engine, Map, INT}; use test::Bencher; #[bench] diff --git a/benches/eval_type.rs b/benches/eval_type.rs index 1e5a70c8..469e6332 100644 --- a/benches/eval_type.rs +++ b/benches/eval_type.rs @@ -3,7 +3,7 @@ ///! Test evaluating expressions extern crate test; -use rhai::{Engine, OptimizationLevel, RegisterFn, Scope, INT}; +use rhai::{Engine, OptimizationLevel, Scope, INT}; use test::Bencher; #[derive(Debug, Clone)] diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 13396a8b..7ba9e874 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -35,7 +35,7 @@ //! # Register a Rust Function with a Rhai `Module` //! //! ``` -//! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn}; +//! use rhai::{EvalAltResult, FLOAT, Module}; //! use rhai::plugin::*; //! use rhai::module_resolvers::*; //! @@ -65,7 +65,7 @@ //! # Register a Plugin Function with an `Engine` //! //! ``` -//! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn}; +//! use rhai::{EvalAltResult, FLOAT, Module}; //! use rhai::plugin::*; //! use rhai::module_resolvers::*; //! diff --git a/codegen/tests/test_functions.rs b/codegen/tests/test_functions.rs index 303076e2..86def42e 100644 --- a/codegen/tests/test_functions.rs +++ b/codegen/tests/test_functions.rs @@ -1,6 +1,6 @@ use rhai::module_resolvers::*; use rhai::plugin::*; -use rhai::{Engine, EvalAltResult, Module, RegisterFn, FLOAT}; +use rhai::{Engine, EvalAltResult, Module, FLOAT}; pub mod raw_fn { use rhai::plugin::*; diff --git a/codegen/tests/test_modules.rs b/codegen/tests/test_modules.rs index 4993c5c3..0e63dad7 100644 --- a/codegen/tests/test_modules.rs +++ b/codegen/tests/test_modules.rs @@ -1,5 +1,5 @@ use rhai::module_resolvers::*; -use rhai::{Array, Engine, EvalAltResult, RegisterFn, FLOAT, INT}; +use rhai::{Array, Engine, EvalAltResult, FLOAT, INT}; pub mod empty_module { use rhai::plugin::*; diff --git a/codegen/tests/test_nested.rs b/codegen/tests/test_nested.rs index 8e3497de..62428282 100644 --- a/codegen/tests/test_nested.rs +++ b/codegen/tests/test_nested.rs @@ -1,5 +1,5 @@ use rhai::module_resolvers::*; -use rhai::{Array, Engine, EvalAltResult, RegisterFn, FLOAT, INT}; +use rhai::{Array, Engine, EvalAltResult, FLOAT, INT}; pub mod one_fn_module_nested_attr { use rhai::plugin::*; diff --git a/examples/arrays_and_structs.rs b/examples/arrays_and_structs.rs index aac28801..374fc834 100644 --- a/examples/arrays_and_structs.rs +++ b/examples/arrays_and_structs.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, INT}; #[derive(Debug, Clone)] struct TestStruct { diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index 5d494fd3..4e06312c 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, INT}; #[derive(Debug, Clone)] struct TestStruct { diff --git a/examples/simple_fn.rs b/examples/simple_fn.rs index c0c59bad..9b9b000f 100644 --- a/examples/simple_fn.rs +++ b/examples/simple_fn.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, INT}; fn add(x: INT, y: INT) -> INT { x + y diff --git a/examples/strings.rs b/examples/strings.rs index 2ea97511..f10fac94 100644 --- a/examples/strings.rs +++ b/examples/strings.rs @@ -1,6 +1,6 @@ ///! This example registers a variety of functions that operate on strings. ///! Remember to use `ImmutableString` or `&str` instead of `String` as parameters. -use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, Scope, INT}; +use rhai::{Engine, EvalAltResult, ImmutableString, Scope, INT}; use std::io::{stdin, stdout, Write}; /// Trim whitespace from a string. The original string argument is changed. diff --git a/examples/threading.rs b/examples/threading.rs index f28da439..2e0aa9d3 100644 --- a/examples/threading.rs +++ b/examples/threading.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, RegisterFn, INT}; +use rhai::{Engine, INT}; #[cfg(feature = "sync")] use std::sync::Mutex; diff --git a/src/engine_api.rs b/src/engine_api.rs index 7218582d..159e822c 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -3,6 +3,7 @@ use crate::dynamic::Variant; use crate::engine::{EvalContext, Imports, State}; use crate::fn_native::{FnCallArgs, SendSync}; +use crate::fn_register::RegisterNativeFunction; use crate::optimize::OptimizationLevel; use crate::stdlib::{ any::{type_name, TypeId}, @@ -24,6 +25,68 @@ use crate::Map; /// Engine public API impl Engine { + /// Register a custom function with the [`Engine`]. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// // Normal function + /// fn add(x: i64, y: i64) -> i64 { + /// x + y + /// } + /// + /// let mut engine = Engine::new(); + /// + /// engine.register_fn("add", add); + /// + /// assert_eq!(engine.eval::("add(40, 2)")?, 42); + /// + /// // You can also register a closure. + /// engine.register_fn("sub", |x: i64, y: i64| x - y ); + /// + /// assert_eq!(engine.eval::("sub(44, 2)")?, 42); + /// # Ok(()) + /// # } + /// ``` + pub fn register_fn(&mut self, name: &str, func: impl RegisterNativeFunction) -> &mut Self { + func.register_into(self, name); + self + } + /// Register a custom fallible function with the [`Engine`]. + /// + /// # Example + /// + /// ``` + /// use rhai::{Engine, Dynamic, EvalAltResult}; + /// + /// // Normal function + /// fn div(x: i64, y: i64) -> Result> { + /// if y == 0 { + /// // '.into()' automatically converts to 'Box' + /// Err("division by zero!".into()) + /// } else { + /// Ok((x / y).into()) + /// } + /// } + /// + /// let mut engine = Engine::new(); + /// + /// engine.register_result_fn("div", div); + /// + /// engine.eval::("div(42, 0)") + /// .expect_err("expecting division by zero error!"); + /// ``` + pub fn register_result_fn( + &mut self, + name: &str, + func: impl RegisterNativeFunction, + ) -> &mut Self { + func.register_into(self, name); + self + } /// Register a function of the [`Engine`]. /// /// # WARNING - Low Level API @@ -76,7 +139,7 @@ impl Engine { /// } /// /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// @@ -115,7 +178,7 @@ impl Engine { /// } /// /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// @@ -177,7 +240,7 @@ impl Engine { /// } /// /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// @@ -199,7 +262,7 @@ impl Engine { name: &str, get_fn: impl Fn(&mut T) -> U + SendSync + 'static, ) -> &mut Self { - use crate::{engine::make_getter, RegisterFn}; + use crate::engine::make_getter; self.register_fn(&make_getter(name), get_fn) } /// Register a getter function for a member of a registered type with the [`Engine`]. @@ -209,7 +272,7 @@ impl Engine { /// # Example /// /// ``` - /// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn}; + /// use rhai::{Engine, Dynamic, EvalAltResult}; /// /// #[derive(Clone)] /// struct TestStruct { @@ -246,7 +309,7 @@ impl Engine { name: &str, get_fn: impl Fn(&mut T) -> RhaiResult + SendSync + 'static, ) -> &mut Self { - use crate::{engine::make_getter, RegisterResultFn}; + use crate::engine::make_getter; self.register_result_fn(&make_getter(name), get_fn) } /// Register a setter function for a member of a registered type with the [`Engine`]. @@ -266,7 +329,7 @@ impl Engine { /// } /// /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// @@ -292,7 +355,7 @@ impl Engine { name: &str, set_fn: impl Fn(&mut T, U) + SendSync + 'static, ) -> &mut Self { - use crate::{engine::make_setter, RegisterFn}; + use crate::engine::make_setter; self.register_fn(&make_setter(name), set_fn) } /// Register a setter function for a member of a registered type with the [`Engine`]. @@ -300,7 +363,7 @@ impl Engine { /// # Example /// /// ``` - /// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn}; + /// use rhai::{Engine, Dynamic, EvalAltResult}; /// /// #[derive(Debug, Clone, Eq, PartialEq)] /// struct TestStruct { @@ -341,7 +404,7 @@ impl Engine { name: &str, set_fn: impl Fn(&mut T, U) -> Result<(), Box> + SendSync + 'static, ) -> &mut Self { - use crate::{engine::make_setter, RegisterResultFn}; + use crate::engine::make_setter; self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| { set_fn(obj, value).map(Into::into) }) @@ -369,7 +432,7 @@ impl Engine { /// } /// /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// @@ -420,7 +483,7 @@ impl Engine { /// } /// /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// @@ -457,8 +520,7 @@ impl Engine { panic!("Cannot register indexer for strings."); } - use crate::{engine::FN_IDX_GET, RegisterFn}; - self.register_fn(FN_IDX_GET, get_fn) + self.register_fn(crate::engine::FN_IDX_GET, get_fn) } /// Register an index getter for a custom type with the [`Engine`]. /// @@ -472,7 +534,7 @@ impl Engine { /// # Example /// /// ``` - /// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn}; + /// use rhai::{Engine, Dynamic, EvalAltResult}; /// /// #[derive(Clone)] /// struct TestStruct { @@ -524,8 +586,7 @@ impl Engine { panic!("Cannot register indexer for strings."); } - use crate::{engine::FN_IDX_GET, RegisterResultFn}; - self.register_result_fn(FN_IDX_GET, get_fn) + self.register_result_fn(crate::engine::FN_IDX_GET, get_fn) } /// Register an index setter for a custom type with the [`Engine`]. /// @@ -549,7 +610,7 @@ impl Engine { /// } /// /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// @@ -589,8 +650,7 @@ impl Engine { panic!("Cannot register indexer for strings."); } - use crate::{engine::FN_IDX_SET, RegisterFn}; - self.register_fn(FN_IDX_SET, set_fn) + self.register_fn(crate::engine::FN_IDX_SET, set_fn) } /// Register an index setter for a custom type with the [`Engine`]. /// @@ -602,7 +662,7 @@ impl Engine { /// # Example /// /// ``` - /// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn}; + /// use rhai::{Engine, Dynamic, EvalAltResult}; /// /// #[derive(Clone)] /// struct TestStruct { @@ -661,10 +721,10 @@ impl Engine { panic!("Cannot register indexer for strings."); } - use crate::{engine::FN_IDX_SET, RegisterResultFn}; - self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| { - set_fn(obj, index, value).map(Into::into) - }) + self.register_result_fn( + crate::engine::FN_IDX_SET, + move |obj: &mut T, index: X, value: U| set_fn(obj, index, value).map(Into::into), + ) } /// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`]. /// @@ -691,7 +751,7 @@ impl Engine { /// } /// /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// diff --git a/src/engine_settings.rs b/src/engine_settings.rs index 7ba0f2cc..1bf32f1a 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -250,7 +250,7 @@ impl Engine { /// /// ```rust /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; + /// use rhai::Engine; /// /// let mut engine = Engine::new(); /// diff --git a/src/fn_native.rs b/src/fn_native.rs index fdb4ba9b..f4d41ae4 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -49,7 +49,7 @@ pub use crate::stdlib::cell::RefCell as Locked; pub use crate::stdlib::sync::RwLock as Locked; /// Context of a native Rust function call. -#[derive(Debug, Copy, Clone)] +#[derive(Debug)] pub struct NativeCallContext<'a> { engine: &'a Engine, fn_name: &'a str, @@ -320,7 +320,7 @@ impl FnPtr { #[inline(always)] pub fn call_dynamic( &self, - ctx: NativeCallContext, + ctx: &NativeCallContext, this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, ) -> RhaiResult { diff --git a/src/fn_register.rs b/src/fn_register.rs index 38f2d272..3d43cc3a 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -4,72 +4,10 @@ use crate::dynamic::{DynamicWriteLock, Variant}; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; -use crate::r#unsafe::unsafe_cast_box; +use crate::r#unsafe::unsafe_try_cast; use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String}; use crate::{Dynamic, Engine, FnAccess, FnNamespace, NativeCallContext, RhaiResult}; -/// Trait to register custom functions with the [`Engine`]. -pub trait RegisterFn { - /// Register a custom function with the [`Engine`]. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, RegisterFn}; - /// - /// // Normal function - /// fn add(x: i64, y: i64) -> i64 { - /// x + y - /// } - /// - /// let mut engine = Engine::new(); - /// - /// // You must use the trait rhai::RegisterFn to get this method. - /// engine.register_fn("add", add); - /// - /// assert_eq!(engine.eval::("add(40, 2)")?, 42); - /// - /// // You can also register a closure. - /// engine.register_fn("sub", |x: i64, y: i64| x - y ); - /// - /// assert_eq!(engine.eval::("sub(44, 2)")?, 42); - /// # Ok(()) - /// # } - /// ``` - fn register_fn(&mut self, name: &str, f: FN) -> &mut Self; -} - -/// Trait to register fallible custom functions returning [`Result`]`<`[`Dynamic`]`, `[`Box`]`<`[`EvalAltResult`][crate::EvalAltResult]`>>` with the [`Engine`]. -pub trait RegisterResultFn { - /// Register a custom fallible function with the [`Engine`]. - /// - /// # Example - /// - /// ``` - /// use rhai::{Engine, Dynamic, RegisterResultFn, EvalAltResult}; - /// - /// // Normal function - /// fn div(x: i64, y: i64) -> Result> { - /// if y == 0 { - /// // '.into()' automatically converts to 'Box' - /// Err("division by zero!".into()) - /// } else { - /// Ok((x / y).into()) - /// } - /// } - /// - /// let mut engine = Engine::new(); - /// - /// // You must use the trait rhai::RegisterResultFn to get this method. - /// engine.register_result_fn("div", div); - /// - /// engine.eval::("div(42, 0)") - /// .expect_err("expecting division by zero error!"); - /// ``` - fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self; -} - // These types are used to build a unique _marker_ tuple type for each combination // of function parameter types in order to make each trait implementation unique. // That is because stable Rust currently does not allow distinguishing implementations @@ -77,7 +15,7 @@ pub trait RegisterResultFn { // // For example: // -// `RegisterFn, B, Ref), R>` +// `NativeFunction<(Mut, B, Ref), R>` // // will have the function prototype constraint to: // @@ -107,7 +45,7 @@ pub fn by_value(data: &mut Dynamic) -> T { ref_t.clone() } else if TypeId::of::() == TypeId::of::() { // If T is `String`, data must be `ImmutableString`, so map directly to it - *unsafe_cast_box(Box::new(mem::take(data).take_string().unwrap())).unwrap() + unsafe_try_cast(mem::take(data).take_string().unwrap()).unwrap() } else { // We consume the argument and then replace it with () - the argument is not supposed to be used again. // This way, we avoid having to clone the argument again, because it is already a clone when passed here. @@ -115,41 +53,10 @@ pub fn by_value(data: &mut Dynamic) -> T { } } -/// This macro creates a closure wrapping a registered function. -macro_rules! make_func { - ($fn:ident : $map:expr ; $($par:ident => $let:stmt => $convert:expr => $arg:expr),*) => { -// ^ function pointer -// ^ result mapping function -// ^ function parameter generic type name (A, B, C etc.) -// ^ argument let statement(e.g. let mut A ...) -// ^ dereferencing function -// ^ argument reference expression(like A, *B, &mut C etc) - - Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { - // The arguments are assumed to be of the correct number and types! - - let mut _drain = args.iter_mut(); - $($let $par = ($convert)(_drain.next().unwrap()); )* - - // Call the function with each argument value - let r = $fn($($arg),*); - - // Map the result - $map(r) - }) as Box - }; -} - -/// To Dynamic mapping function. -#[inline(always)] -pub fn map_dynamic(data: impl Variant + Clone) -> RhaiResult { - Ok(data.into_dynamic()) -} - -/// To Dynamic mapping function. -#[inline(always)] -pub fn map_result(data: RhaiResult) -> RhaiResult { - data +/// Trait to register custom functions with an [`Engine`]. +pub trait RegisterNativeFunction { + /// Register the function with an [`Engine`]. + fn register_into(self, engine: &mut Engine, name: &str); } macro_rules! def_register { @@ -160,37 +67,95 @@ macro_rules! def_register { // ^ function ABI type // ^ function parameter generic type name (A, B, C etc.) // ^ call argument(like A, *B, &mut C etc) - // ^ function parameter marker type (T, Ref or Mut) + // ^ function parameter marker type (T, Ref or Mut) // ^ function parameter actual type (T, &T or &mut T) // ^ argument let statement + impl< - $($par: Variant + Clone,)* FN: Fn($($param),*) -> RET + SendSync + 'static, + $($par: Variant + Clone,)* RET: Variant + Clone - > RegisterFn for Engine - { + > RegisterNativeFunction<($($mark,)*), ()> for FN { #[inline(always)] - fn register_fn(&mut self, name: &str, f: FN) -> &mut Self { - self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, + fn register_into(self, engine: &mut Engine, name: &str) { + engine.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, &[$(TypeId::of::<$par>()),*], - CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*)) + CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { + // The arguments are assumed to be of the correct number and types! + let mut _drain = args.iter_mut(); + $($let $par = ($clone)(_drain.next().unwrap()); )* + + // Call the function with each argument value + let r = self($($arg),*); + + // Map the result + Ok(r.into_dynamic()) + }) as Box) ); - self } } impl< + FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RET + SendSync + 'static, $($par: Variant + Clone,)* - FN: Fn($($param),*) -> RhaiResult + SendSync + 'static, - > RegisterResultFn for Engine - { + RET: Variant + Clone + > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN { #[inline(always)] - fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self { - self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, + fn register_into(self, engine: &mut Engine, name: &str) { + engine.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, &[$(TypeId::of::<$par>()),*], - CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*)) + CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { + // The arguments are assumed to be of the correct number and types! + let mut _drain = args.iter_mut(); + $($let $par = ($clone)(_drain.next().unwrap()); )* + + // Call the function with each argument value + let r = self(ctx, $($arg),*); + + // Map the result + Ok(r.into_dynamic()) + }) as Box) + ); + } + } + + impl< + FN: Fn($($param),*) -> RhaiResult + SendSync + 'static, + $($par: Variant + Clone,)* + > RegisterNativeFunction<($($mark,)*), RhaiResult> for FN { + #[inline(always)] + fn register_into(self, engine: &mut Engine, name: &str) { + engine.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, + &[$(TypeId::of::<$par>()),*], + CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { + // The arguments are assumed to be of the correct number and types! + let mut _drain = args.iter_mut(); + $($let $par = ($clone)(_drain.next().unwrap()); )* + + // Call the function with each argument value + self($($arg),*) + }) as Box) + ); + } + } + + impl< + FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RhaiResult + SendSync + 'static, + $($par: Variant + Clone,)* + > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), RhaiResult> for FN { + #[inline(always)] + fn register_into(self, engine: &mut Engine, name: &str) { + engine.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, + &[$(TypeId::of::<$par>()),*], + CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { + // The arguments are assumed to be of the correct number and types! + let mut _drain = args.iter_mut(); + $($let $par = ($clone)(_drain.next().unwrap()); )* + + // Call the function with each argument value + self(ctx, $($arg),*) + }) as Box) ); - self } } diff --git a/src/lib.rs b/src/lib.rs index 503890d5..215db356 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ //! ## The Rust part //! //! ```,no_run -//! use rhai::{Engine, EvalAltResult, RegisterFn}; +//! use rhai::{Engine, EvalAltResult}; //! //! fn main() -> Result<(), Box> //! { @@ -126,7 +126,7 @@ pub use ast::{FnAccess, AST}; pub use dynamic::Dynamic; pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS}; pub use fn_native::{FnPtr, NativeCallContext}; -pub use fn_register::{RegisterFn, RegisterResultFn}; +pub use fn_register::RegisterNativeFunction; pub use module::{FnNamespace, Module}; pub use parse_error::{LexError, ParseError, ParseErrorType}; pub use result::EvalAltResult; diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 7e3012f9..5244c46d 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -175,12 +175,12 @@ mod array_functions { for (i, item) in array.iter().enumerate() { ar.push( mapper - .call_dynamic(ctx, None, [item.clone()]) + .call_dynamic(&ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(mapper.fn_name()) => { - mapper.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) + mapper.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -207,12 +207,12 @@ mod array_functions { for (i, item) in array.iter().enumerate() { if filter - .call_dynamic(ctx, None, [item.clone()]) + .call_dynamic(&ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -303,12 +303,12 @@ mod array_functions { ) -> Result> { for (i, item) in array.iter().enumerate() { if filter - .call_dynamic(ctx, None, [item.clone()]) + .call_dynamic(&ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -337,12 +337,12 @@ mod array_functions { ) -> Result> { for (i, item) in array.iter().enumerate() { if filter - .call_dynamic(ctx, None, [item.clone()]) + .call_dynamic(&ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -371,12 +371,12 @@ mod array_functions { ) -> Result> { for (i, item) in array.iter().enumerate() { if !filter - .call_dynamic(ctx, None, [item.clone()]) + .call_dynamic(&ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -407,12 +407,12 @@ mod array_functions { for (i, item) in array.iter().enumerate() { result = reducer - .call_dynamic(ctx, None, [result.clone(), item.clone()]) + .call_dynamic(&ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { - reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()]) + reducer.call_dynamic(&ctx, None, [result, item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -435,7 +435,7 @@ mod array_functions { reducer: FnPtr, initial: FnPtr, ) -> Result> { - let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| { + let mut result = initial.call_dynamic(&ctx, None, []).map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), ctx.source().unwrap_or("").to_string(), @@ -446,12 +446,12 @@ mod array_functions { for (i, item) in array.iter().enumerate() { result = reducer - .call_dynamic(ctx, None, [result.clone(), item.clone()]) + .call_dynamic(&ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { - reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()]) + reducer.call_dynamic(&ctx, None, [result, item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -477,12 +477,12 @@ mod array_functions { for (i, item) in array.iter().enumerate().rev() { result = reducer - .call_dynamic(ctx, None, [result.clone(), item.clone()]) + .call_dynamic(&ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { - reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()]) + reducer.call_dynamic(&ctx, None, [result, item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -505,7 +505,7 @@ mod array_functions { reducer: FnPtr, initial: FnPtr, ) -> Result> { - let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| { + let mut result = initial.call_dynamic(&ctx, None, []).map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce_rev".to_string(), ctx.source().unwrap_or("").to_string(), @@ -516,12 +516,12 @@ mod array_functions { for (i, item) in array.iter().enumerate().rev() { result = reducer - .call_dynamic(ctx, None, [result.clone(), item.clone()]) + .call_dynamic(&ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { - reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()]) + reducer.call_dynamic(&ctx, None, [result, item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -545,7 +545,7 @@ mod array_functions { ) -> Result> { array.sort_by(|x, y| { comparer - .call_dynamic(ctx, None, [x.clone(), y.clone()]) + .call_dynamic(&ctx, None, [x.clone(), y.clone()]) .ok() .and_then(|v| v.as_int().ok()) .map(|v| { @@ -587,12 +587,12 @@ mod array_functions { i -= 1; if filter - .call_dynamic(ctx, None, [array[i].clone()]) + .call_dynamic(&ctx, None, [array[i].clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(ctx, None, [array[i].clone(), (i as INT).into()]) + filter.call_dynamic(&ctx, None, [array[i].clone(), (i as INT).into()]) } _ => Err(err), }) @@ -647,12 +647,12 @@ mod array_functions { i -= 1; if !filter - .call_dynamic(ctx, None, [array[i].clone()]) + .call_dynamic(&ctx, None, [array[i].clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(ctx, None, [array[i].clone(), (i as INT).into()]) + filter.call_dynamic(&ctx, None, [array[i].clone(), (i as INT).into()]) } _ => Err(err), }) diff --git a/src/plugin.rs b/src/plugin.rs index 8aa2d8e1..d7013ef7 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -5,7 +5,7 @@ pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, use crate::RhaiResult; pub use crate::{ Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module, - NativeCallContext, Position, RegisterFn, RegisterResultFn, + NativeCallContext, Position, }; #[cfg(not(features = "no_module"))] diff --git a/tests/arrays.rs b/tests/arrays.rs index 78cf30b6..1be7f8cb 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "no_index"))] -use rhai::{Array, Engine, EvalAltResult, RegisterFn, INT}; +use rhai::{Array, Engine, EvalAltResult, INT}; #[test] fn test_arrays() -> Result<(), Box> { diff --git a/tests/call_fn.rs b/tests/call_fn.rs index f7e4fa67..dbe3ece0 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "no_function"))] -use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Func, FuncArgs, RegisterFn, Scope, INT}; +use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Func, FuncArgs, Scope, INT}; use std::{any::TypeId, iter::once}; #[test] @@ -131,7 +131,7 @@ fn test_fn_ptr_raw() -> Result<(), Box> { let value = args[2].clone(); let this_ptr = args.get_mut(0).unwrap(); - fp.call_dynamic(context, Some(this_ptr), [value]) + fp.call_dynamic(&context, Some(this_ptr), [value]) }, ); diff --git a/tests/closures.rs b/tests/closures.rs index 25f81c8a..ff4cc431 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -1,7 +1,5 @@ #![cfg(not(feature = "no_function"))] -use rhai::{ - Engine, EvalAltResult, FnPtr, NativeCallContext, ParseErrorType, RegisterFn, Scope, INT, -}; +use rhai::{Engine, EvalAltResult, FnPtr, NativeCallContext, ParseErrorType, Scope, INT}; use std::any::TypeId; use std::cell::RefCell; use std::mem::take; @@ -20,7 +18,7 @@ fn test_fn_ptr_curry_call() -> Result<(), Box> { &[TypeId::of::(), TypeId::of::()], |context, args| { let fn_ptr = std::mem::take(args[0]).cast::(); - fn_ptr.call_dynamic(context, None, [std::mem::take(args[1])]) + fn_ptr.call_dynamic(&context, None, [std::mem::take(args[1])]) }, ); @@ -159,7 +157,7 @@ fn test_closures() -> Result<(), Box> { |context, args| { let func = take(args[1]).cast::(); - func.call_dynamic(context, None, []) + func.call_dynamic(&context, None, []) }, ); @@ -343,7 +341,7 @@ fn test_closures_external() -> Result<(), Box> { let context = NativeCallContext::new(&engine, &fn_name, &lib); // Closure 'f' captures: the engine, the AST, and the curried function pointer - let f = move |x: INT| fn_ptr.call_dynamic(context, None, [x.into()]); + let f = move |x: INT| fn_ptr.call_dynamic(&context, None, [x.into()]); assert_eq!(f(42)?.take_string(), Ok("hello42".to_string())); diff --git a/tests/constants.rs b/tests/constants.rs index 05ec5ea2..b3432449 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, ParseErrorType, RegisterFn, Scope, INT}; +use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT}; #[test] fn test_constant() -> Result<(), Box> { diff --git a/tests/float.rs b/tests/float.rs index 5cb23e5f..a1c05186 100644 --- a/tests/float.rs +++ b/tests/float.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "no_float"))] -use rhai::{Engine, EvalAltResult, RegisterFn, FLOAT}; +use rhai::{Engine, EvalAltResult, FLOAT}; const EPSILON: FLOAT = 0.000_000_000_1; diff --git a/tests/fn_ptr.rs b/tests/fn_ptr.rs index 9f175bd2..04d89b29 100644 --- a/tests/fn_ptr.rs +++ b/tests/fn_ptr.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_fn_ptr() -> Result<(), Box> { diff --git a/tests/functions.rs b/tests/functions.rs index f5624fbf..7f8fe26c 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "no_function"))] -use rhai::{Engine, EvalAltResult, FnNamespace, Module, ParseErrorType, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, FnNamespace, Module, ParseErrorType, INT}; #[test] fn test_functions() -> Result<(), Box> { diff --git a/tests/get_set.rs b/tests/get_set.rs index b654348c..79acc67d 100644 --- a/tests/get_set.rs +++ b/tests/get_set.rs @@ -1,6 +1,6 @@ #![cfg(not(feature = "no_object"))] -use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, ImmutableString, INT}; #[test] fn test_get_set() -> Result<(), Box> { diff --git a/tests/method_call.rs b/tests/method_call.rs index e7b863e6..a65b511a 100644 --- a/tests/method_call.rs +++ b/tests/method_call.rs @@ -1,6 +1,6 @@ #![cfg(not(feature = "no_object"))] -use rhai::{Engine, EvalAltResult, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_method_call() -> Result<(), Box> { diff --git a/tests/mismatched_op.rs b/tests/mismatched_op.rs index eea4f693..1f259484 100644 --- a/tests/mismatched_op.rs +++ b/tests/mismatched_op.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_mismatched_op() { diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 7f9d2bd5..caa98dcc 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -1,6 +1,6 @@ #![cfg(not(feature = "no_optimize"))] -use rhai::{Engine, EvalAltResult, OptimizationLevel, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, OptimizationLevel, INT}; #[test] fn test_optimizer_run() -> Result<(), Box> { diff --git a/tests/print.rs b/tests/print.rs index d2679073..d574d5fe 100644 --- a/tests/print.rs +++ b/tests/print.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT}; +use rhai::{Engine, EvalAltResult, Scope, INT}; use std::sync::{Arc, RwLock}; #[cfg(not(feature = "only_i32"))] diff --git a/tests/side_effects.rs b/tests/side_effects.rs index b7ba49f6..52caabd2 100644 --- a/tests/side_effects.rs +++ b/tests/side_effects.rs @@ -1,5 +1,5 @@ ///! This test simulates an external command object that is driven by a script. -use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT}; +use rhai::{Engine, EvalAltResult, Scope, INT}; use std::sync::{Arc, Mutex, RwLock}; /// Simulate a command object. diff --git a/tests/string.rs b/tests/string.rs index 2078b90c..ff787116 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, Scope, INT}; +use rhai::{Engine, EvalAltResult, ImmutableString, Scope, INT}; #[test] fn test_string() -> Result<(), Box> { diff --git a/tests/tokens.rs b/tests/tokens.rs index 71158313..b8343a69 100644 --- a/tests/tokens.rs +++ b/tests/tokens.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, INT}; #[test] fn test_tokens_disabled() { diff --git a/tests/types.rs b/tests/types.rs index 2ee51a68..87e93d51 100644 --- a/tests/types.rs +++ b/tests/types.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, RegisterFn, INT}; +use rhai::{Engine, EvalAltResult, INT}; #[test] fn test_type_of() -> Result<(), Box> { From e9f280f91705a23ce7ecb6f325fe601752120441 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 15 Mar 2021 12:39:06 +0800 Subject: [PATCH 03/15] Deprecate Module::set_fn_XXX API. --- CHANGELOG.md | 1 + src/engine_api.rs | 37 ++- src/fn_register.rs | 114 ++++----- src/module/mod.rs | 495 +++++-------------------------------- src/packages/iter_basic.rs | 8 +- src/packages/mod.rs | 5 +- tests/for.rs | 2 +- tests/functions.rs | 2 +- tests/modules.rs | 19 +- 9 files changed, 169 insertions(+), 514 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a841377..9957e59e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Breaking changes * The traits `RegisterFn` and `RegisterResultFn` are removed. `Engine::register_fn` and `Engine::register_result_fn` are now implemented directly on `Engine`. * `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it. +* All `Module::set_fn_XXX` methods are removed, in favor of `Module::set_native_fn`. Version 0.19.14 diff --git a/src/engine_api.rs b/src/engine_api.rs index 159e822c..eb6b97f5 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -51,8 +51,18 @@ impl Engine { /// # Ok(()) /// # } /// ``` - pub fn register_fn(&mut self, name: &str, func: impl RegisterNativeFunction) -> &mut Self { - func.register_into(self, name); + pub fn register_fn(&mut self, name: &str, func: F) -> &mut Self + where + F: RegisterNativeFunction, + { + self.global_namespace.set_fn( + name, + FnNamespace::Global, + FnAccess::Public, + None, + &F::param_types(), + func.into_callable_function(), + ); self } /// Register a custom fallible function with the [`Engine`]. @@ -79,12 +89,18 @@ impl Engine { /// engine.eval::("div(42, 0)") /// .expect_err("expecting division by zero error!"); /// ``` - pub fn register_result_fn( - &mut self, - name: &str, - func: impl RegisterNativeFunction, - ) -> &mut Self { - func.register_into(self, name); + pub fn register_result_fn(&mut self, name: &str, func: F) -> &mut Self + where + F: RegisterNativeFunction, + { + self.global_namespace.set_fn( + name, + FnNamespace::Global, + FnAccess::Public, + None, + &F::param_types(), + func.into_callable_function(), + ); self } /// Register a function of the [`Engine`]. @@ -803,8 +819,7 @@ impl Engine { pub fn load_package(&mut self, module: impl Into>) -> &mut Self { self.register_global_module(module.into()) } - /// Register a shared [`Module`] as a static module namespace with the - /// [`Engine`]. + /// Register a shared [`Module`] as a static module namespace with the [`Engine`]. /// /// Functions marked [`FnNamespace::Global`] and type iterators are exposed to scripts without /// namespace qualifications. @@ -819,7 +834,7 @@ impl Engine { /// /// // Create the module /// let mut module = Module::new(); - /// module.set_fn_1("calc", |x: i64| Ok(x + 1)); + /// module.set_native_fn("calc", |x: i64| Ok(x + 1)); /// /// let module: Shared = module.into(); /// diff --git a/src/fn_register.rs b/src/fn_register.rs index 3d43cc3a..f53157a0 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -5,8 +5,8 @@ use crate::dynamic::{DynamicWriteLock, Variant}; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::r#unsafe::unsafe_try_cast; -use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String}; -use crate::{Dynamic, Engine, FnAccess, FnNamespace, NativeCallContext, RhaiResult}; +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 // of function parameter types in order to make each trait implementation unique. @@ -53,10 +53,12 @@ pub fn by_value(data: &mut Dynamic) -> T { } } -/// Trait to register custom functions with an [`Engine`]. +/// Trait to register custom Rust functions. pub trait RegisterNativeFunction { - /// Register the function with an [`Engine`]. - fn register_into(self, engine: &mut Engine, name: &str); + /// Get the types of this function's parameters. + fn param_types() -> Box<[TypeId]>; + /// Convert this function into a [`CallableFunction`]. + fn into_callable_function(self) -> CallableFunction; } macro_rules! def_register { @@ -77,21 +79,20 @@ macro_rules! def_register { RET: Variant + Clone > RegisterNativeFunction<($($mark,)*), ()> for FN { #[inline(always)] - fn register_into(self, engine: &mut Engine, name: &str) { - engine.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, - &[$(TypeId::of::<$par>()),*], - CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { - // The arguments are assumed to be of the correct number and types! - let mut _drain = args.iter_mut(); - $($let $par = ($clone)(_drain.next().unwrap()); )* + fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } + #[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! + let mut _drain = args.iter_mut(); + $($let $par = ($clone)(_drain.next().unwrap()); )* - // Call the function with each argument value - let r = self($($arg),*); + // Call the function with each argument value + let r = self($($arg),*); - // Map the result - Ok(r.into_dynamic()) - }) as Box) - ); + // Map the result + Ok(r.into_dynamic()) + }) as Box) } } @@ -101,61 +102,60 @@ macro_rules! def_register { RET: Variant + Clone > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN { #[inline(always)] - fn register_into(self, engine: &mut Engine, name: &str) { - engine.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, - &[$(TypeId::of::<$par>()),*], - CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { - // The arguments are assumed to be of the correct number and types! - let mut _drain = args.iter_mut(); - $($let $par = ($clone)(_drain.next().unwrap()); )* + fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } + #[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! + let mut _drain = args.iter_mut(); + $($let $par = ($clone)(_drain.next().unwrap()); )* - // Call the function with each argument value - let r = self(ctx, $($arg),*); + // Call the function with each argument value + let r = self(ctx, $($arg),*); - // Map the result - Ok(r.into_dynamic()) - }) as Box) - ); + // Map the result + Ok(r.into_dynamic()) + }) as Box) } } impl< - FN: Fn($($param),*) -> RhaiResult + SendSync + 'static, + FN: Fn($($param),*) -> Result> + SendSync + 'static, $($par: Variant + Clone,)* - > RegisterNativeFunction<($($mark,)*), RhaiResult> for FN { + RET: Variant + Clone + > RegisterNativeFunction<($($mark,)*), Result>> for FN { #[inline(always)] - fn register_into(self, engine: &mut Engine, name: &str) { - engine.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, - &[$(TypeId::of::<$par>()),*], - CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { - // The arguments are assumed to be of the correct number and types! - let mut _drain = args.iter_mut(); - $($let $par = ($clone)(_drain.next().unwrap()); )* + fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } + #[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! + let mut _drain = args.iter_mut(); + $($let $par = ($clone)(_drain.next().unwrap()); )* - // Call the function with each argument value - self($($arg),*) - }) as Box) - ); + // Call the function with each argument value + self($($arg),*).map(Dynamic::from) + }) as Box) } } impl< - FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RhaiResult + SendSync + 'static, + FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> Result> + SendSync + 'static, $($par: Variant + Clone,)* - > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), RhaiResult> for FN { + RET: Variant + Clone + > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), Result>> for FN { #[inline(always)] - fn register_into(self, engine: &mut Engine, name: &str) { - engine.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, - &[$(TypeId::of::<$par>()),*], - CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { - // The arguments are assumed to be of the correct number and types! - let mut _drain = args.iter_mut(); - $($let $par = ($clone)(_drain.next().unwrap()); )* + fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } + #[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! + let mut _drain = args.iter_mut(); + $($let $par = ($clone)(_drain.next().unwrap()); )* - // Call the function with each argument value - self(ctx, $($arg),*) - }) as Box) - ); + // Call the function with each argument value + self(ctx, $($arg),*).map(Dynamic::from) + }) as Box) } } diff --git a/src/module/mod.rs b/src/module/mod.rs index 18c7f3f1..73e6aeea 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -3,7 +3,7 @@ use crate::ast::{FnAccess, Ident}; use crate::dynamic::Variant; use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; -use crate::fn_register::by_value as cast_arg; +use crate::fn_register::RegisterNativeFunction; use crate::stdlib::{ any::TypeId, boxed::Box, @@ -607,7 +607,7 @@ impl Module { /// Does the particular Rust function exist in the [`Module`]? /// - /// The [`u64`] hash is returned by the `set_fn_XXX` calls. + /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. /// /// # Example /// @@ -615,7 +615,7 @@ impl Module { /// use rhai::Module; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_0("calc", || Ok(42_i64)); + /// let hash = module.set_native_fn("calc", || Ok(42_i64)); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline(always)] @@ -634,7 +634,7 @@ impl Module { /// Update the metadata (parameter names/types and return type) of a registered function. /// - /// The [`u64`] hash is returned by the `set_fn_XXX` calls. + /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. /// /// ## Parameter Names and Types /// @@ -654,7 +654,7 @@ impl Module { /// Update the namespace of a registered function. /// - /// The [`u64`] hash is returned by the `set_fn_XXX` calls. + /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. #[inline(always)] pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { if let Some(f) = self.functions.get_mut(&hash_fn) { @@ -822,13 +822,19 @@ impl Module { ) } - /// Set a Rust function taking no parameters into the [`Module`], returning a hash key. + /// Set a Rust function into the [`Module`], returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Namespace + /// + /// The default function namespace is [`FnNamespace::Internal`]. + /// Use [`update_fn_namespace`][Module::update_fn_namespace] to change it. + /// /// # Function Metadata /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// No metadata for the function is registered. + /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata. /// /// # Example /// @@ -836,101 +842,22 @@ impl Module { /// use rhai::Module; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_0("calc", || Ok(42_i64)); + /// let hash = module.set_native_fn("calc", || Ok(42_i64)); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline(always)] - pub fn set_fn_0( - &mut self, - name: impl Into, - func: impl Fn() -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from); - let arg_types = []; + pub fn set_native_fn(&mut self, name: impl Into, func: F) -> u64 + where + T: Variant + Clone, + F: RegisterNativeFunction>>, + { self.set_fn( name, FnNamespace::Internal, FnAccess::Public, None, - &arg_types, - CallableFunction::from_pure(Box::new(f)), - ) - } - - /// Set a Rust function taking one parameter into the [`Module`], returning a hash key. - /// - /// If there is a similar existing Rust function, it is replaced. - /// - /// # Function Metadata - /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. - /// - /// # Example - /// - /// ``` - /// use rhai::Module; - /// - /// let mut module = Module::new(); - /// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1)); - /// assert!(module.contains_fn(hash, true)); - /// ``` - #[inline(always)] - pub fn set_fn_1( - &mut self, - name: impl Into, - func: impl Fn(A) -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - func(cast_arg::(&mut args[0])).map(Dynamic::from) - }; - let arg_types = [TypeId::of::()]; - self.set_fn( - name, - FnNamespace::Internal, - FnAccess::Public, - None, - &arg_types, - CallableFunction::from_pure(Box::new(f)), - ) - } - - /// Set a Rust function taking one mutable parameter into the [`Module`], returning a hash key. - /// - /// If there is a similar existing Rust function, it is replaced. - /// - /// # Function Metadata - /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. - /// - /// # Example - /// - /// ``` - /// use rhai::{Module, FnNamespace}; - /// - /// let mut module = Module::new(); - /// let hash = module.set_fn_1_mut("calc", FnNamespace::Internal, - /// |x: &mut i64| { *x += 1; Ok(*x) } - /// ); - /// assert!(module.contains_fn(hash, true)); - /// ``` - #[inline(always)] - pub fn set_fn_1_mut( - &mut self, - name: impl Into, - namespace: FnNamespace, - func: impl Fn(&mut A) -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - func(&mut args[0].write_lock::().unwrap()).map(Dynamic::from) - }; - let arg_types = [TypeId::of::()]; - self.set_fn( - name, - namespace, - FnAccess::Public, - None, - &arg_types, - CallableFunction::from_method(Box::new(f)), + &F::param_types(), + func.into_callable_function(), ) } @@ -954,104 +881,20 @@ impl Module { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn set_getter_fn( - &mut self, - name: impl Into, - func: impl Fn(&mut A) -> Result> + SendSync + 'static, - ) -> u64 { - self.set_fn_1_mut( + pub fn set_getter_fn(&mut self, name: impl Into, func: F) -> u64 + where + A: Variant + Clone, + T: Variant + Clone, + F: RegisterNativeFunction>>, + F: Fn(&mut A) -> Result> + SendSync + 'static, + { + self.set_fn( crate::engine::make_getter(&name.into()), FnNamespace::Global, - func, - ) - } - - /// Set a Rust function taking two parameters into the [`Module`], returning a hash key. - /// - /// If there is a similar existing Rust function, it is replaced. - /// - /// # Function Metadata - /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. - /// - /// # Example - /// - /// ``` - /// use rhai::{Module, ImmutableString}; - /// - /// let mut module = Module::new(); - /// let hash = module.set_fn_2("calc", |x: i64, y: ImmutableString| { - /// Ok(x + y.len() as i64) - /// }); - /// assert!(module.contains_fn(hash, true)); - /// ``` - #[inline(always)] - pub fn set_fn_2( - &mut self, - name: impl Into, - func: impl Fn(A, B) -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - let a = cast_arg::(&mut args[0]); - let b = cast_arg::(&mut args[1]); - - func(a, b).map(Dynamic::from) - }; - let arg_types = [TypeId::of::(), TypeId::of::()]; - self.set_fn( - name, - FnNamespace::Internal, FnAccess::Public, None, - &arg_types, - CallableFunction::from_pure(Box::new(f)), - ) - } - - /// Set a Rust function taking two parameters (the first one mutable) into the [`Module`], - /// returning a hash key. - /// - /// If there is a similar existing Rust function, it is replaced. - /// - /// # Function Metadata - /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. - /// - /// # Example - /// - /// ``` - /// use rhai::{Module, FnNamespace, ImmutableString}; - /// - /// let mut module = Module::new(); - /// let hash = module.set_fn_2_mut("calc", FnNamespace::Internal, - /// |x: &mut i64, y: ImmutableString| { - /// *x += y.len() as i64; - /// Ok(*x) - /// } - /// ); - /// assert!(module.contains_fn(hash, true)); - /// ``` - #[inline(always)] - pub fn set_fn_2_mut( - &mut self, - name: impl Into, - namespace: FnNamespace, - func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - let b = cast_arg::(&mut args[1]); - let a = &mut args[0].write_lock::().unwrap(); - - func(a, b).map(Dynamic::from) - }; - let arg_types = [TypeId::of::(), TypeId::of::()]; - self.set_fn( - name, - namespace, - FnAccess::Public, - None, - &arg_types, - CallableFunction::from_method(Box::new(f)), + &F::param_types(), + func.into_callable_function(), ) } @@ -1079,15 +922,20 @@ impl Module { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn set_setter_fn( - &mut self, - name: impl Into, - func: impl Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, - ) -> u64 { - self.set_fn_2_mut( + pub fn set_setter_fn(&mut self, name: impl Into, func: F) -> u64 + where + A: Variant + Clone, + B: Variant + Clone, + F: RegisterNativeFunction>>, + F: Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, + { + self.set_fn( crate::engine::make_setter(&name.into()), FnNamespace::Global, - func, + FnAccess::Public, + None, + &F::param_types(), + func.into_callable_function(), ) } @@ -1119,10 +967,14 @@ impl Module { /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn set_indexer_get_fn( - &mut self, - func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, - ) -> u64 { + pub fn set_indexer_get_fn(&mut self, func: F) -> u64 + where + A: Variant + Clone, + B: Variant + Clone, + T: Variant + Clone, + F: RegisterNativeFunction>>, + F: Fn(&mut A, B) -> Result> + SendSync + 'static, + { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } @@ -1137,107 +989,13 @@ impl Module { panic!("Cannot register indexer for strings."); } - self.set_fn_2_mut(crate::engine::FN_IDX_GET, FnNamespace::Global, func) - } - - /// Set a Rust function taking three parameters into the [`Module`], returning a hash key. - /// - /// If there is a similar existing Rust function, it is replaced. - /// - /// # Function Metadata - /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. - /// - /// # Example - /// - /// ``` - /// use rhai::{Module, ImmutableString}; - /// - /// let mut module = Module::new(); - /// let hash = module.set_fn_3("calc", |x: i64, y: ImmutableString, z: i64| { - /// Ok(x + y.len() as i64 + z) - /// }); - /// assert!(module.contains_fn(hash, true)); - /// ``` - #[inline(always)] - pub fn set_fn_3< - A: Variant + Clone, - B: Variant + Clone, - C: Variant + Clone, - T: Variant + Clone, - >( - &mut self, - name: impl Into, - func: impl Fn(A, B, C) -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - let a = cast_arg::(&mut args[0]); - let b = cast_arg::(&mut args[1]); - let c = cast_arg::(&mut args[2]); - - func(a, b, c).map(Dynamic::from) - }; - let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( - name, - FnNamespace::Internal, + crate::engine::FN_IDX_GET, + FnNamespace::Global, FnAccess::Public, None, - &arg_types, - CallableFunction::from_pure(Box::new(f)), - ) - } - - /// Set a Rust function taking three parameters (the first one mutable) into the [`Module`], - /// returning a hash key. - /// - /// If there is a similar existing Rust function, it is replaced. - /// - /// # Function Metadata - /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. - /// - /// # Example - /// - /// ``` - /// use rhai::{Module, FnNamespace, ImmutableString}; - /// - /// let mut module = Module::new(); - /// let hash = module.set_fn_3_mut("calc", FnNamespace::Internal, - /// |x: &mut i64, y: ImmutableString, z: i64| { - /// *x += y.len() as i64 + z; - /// Ok(*x) - /// } - /// ); - /// assert!(module.contains_fn(hash, true)); - /// ``` - #[inline(always)] - pub fn set_fn_3_mut< - A: Variant + Clone, - B: Variant + Clone, - C: Variant + Clone, - T: Variant + Clone, - >( - &mut self, - name: impl Into, - namespace: FnNamespace, - func: impl Fn(&mut A, B, C) -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - let b = cast_arg::(&mut args[2]); - let c = cast_arg::(&mut args[3]); - let a = &mut args[0].write_lock::().unwrap(); - - func(a, b, c).map(Dynamic::from) - }; - let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; - self.set_fn( - name, - namespace, - FnAccess::Public, - None, - &arg_types, - CallableFunction::from_method(Box::new(f)), + &F::param_types(), + func.into_callable_function(), ) } @@ -1270,10 +1028,14 @@ impl Module { /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn set_indexer_set_fn( - &mut self, - func: impl Fn(&mut A, B, C) -> Result<(), Box> + SendSync + 'static, - ) -> u64 { + pub fn set_indexer_set_fn(&mut self, func: F) -> u64 + where + A: Variant + Clone, + B: Variant + Clone, + C: Variant + Clone, + F: RegisterNativeFunction>>, + F: Fn(&mut A, B, C) -> Result<(), Box> + SendSync + 'static, + { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } @@ -1288,21 +1050,13 @@ impl Module { panic!("Cannot register indexer for strings."); } - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - let b = cast_arg::(&mut args[1]); - let c = cast_arg::(&mut args[2]); - let a = &mut args[0].write_lock::().unwrap(); - - func(a, b, c).map(Dynamic::from) - }; - let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( crate::engine::FN_IDX_SET, - FnNamespace::Internal, + FnNamespace::Global, FnAccess::Public, None, - &arg_types, - CallableFunction::from_method(Box::new(f)), + &F::param_types(), + func.into_callable_function(), ) } @@ -1352,124 +1106,9 @@ impl Module { ) } - /// Set a Rust function taking four parameters into the [`Module`], returning a hash key. - /// - /// If there is a similar existing Rust function, it is replaced. - /// - /// # Function Metadata - /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. - /// - /// # Example - /// - /// ``` - /// use rhai::{Module, ImmutableString}; - /// - /// let mut module = Module::new(); - /// let hash = module.set_fn_4("calc", |x: i64, y: ImmutableString, z: i64, _w: ()| { - /// Ok(x + y.len() as i64 + z) - /// }); - /// assert!(module.contains_fn(hash, true)); - /// ``` - #[inline(always)] - pub fn set_fn_4< - A: Variant + Clone, - B: Variant + Clone, - C: Variant + Clone, - D: Variant + Clone, - T: Variant + Clone, - >( - &mut self, - name: impl Into, - func: impl Fn(A, B, C, D) -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - let a = cast_arg::(&mut args[0]); - let b = cast_arg::(&mut args[1]); - let c = cast_arg::(&mut args[2]); - let d = cast_arg::(&mut args[3]); - - func(a, b, c, d).map(Dynamic::from) - }; - let arg_types = [ - TypeId::of::(), - TypeId::of::(), - TypeId::of::(), - TypeId::of::(), - ]; - self.set_fn( - name, - FnNamespace::Internal, - FnAccess::Public, - None, - &arg_types, - CallableFunction::from_pure(Box::new(f)), - ) - } - - /// Set a Rust function taking four parameters (the first one mutable) into the [`Module`], - /// returning a hash key. - /// - /// If there is a similar existing Rust function, it is replaced. - /// - /// # Function Metadata - /// - /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. - /// - /// # Example - /// - /// ``` - /// use rhai::{Module, FnNamespace, ImmutableString}; - /// - /// let mut module = Module::new(); - /// let hash = module.set_fn_4_mut("calc", FnNamespace::Internal, - /// |x: &mut i64, y: ImmutableString, z: i64, _w: ()| { - /// *x += y.len() as i64 + z; - /// Ok(*x) - /// } - /// ); - /// assert!(module.contains_fn(hash, true)); - /// ``` - #[inline(always)] - pub fn set_fn_4_mut< - A: Variant + Clone, - B: Variant + Clone, - C: Variant + Clone, - D: Variant + Clone, - T: Variant + Clone, - >( - &mut self, - name: impl Into, - namespace: FnNamespace, - func: impl Fn(&mut A, B, C, D) -> Result> + SendSync + 'static, - ) -> u64 { - let f = move |_: NativeCallContext, args: &mut FnCallArgs| { - let b = cast_arg::(&mut args[1]); - let c = cast_arg::(&mut args[2]); - let d = cast_arg::(&mut args[3]); - let a = &mut args[0].write_lock::().unwrap(); - - func(a, b, c, d).map(Dynamic::from) - }; - let arg_types = [ - TypeId::of::(), - TypeId::of::(), - TypeId::of::(), - TypeId::of::(), - ]; - self.set_fn( - name, - namespace, - FnAccess::Public, - None, - &arg_types, - CallableFunction::from_method(Box::new(f)), - ) - } - /// Get a Rust function. /// - /// The [`u64`] hash is returned by the `set_fn_XXX` calls. + /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. #[inline(always)] pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> { self.functions.get(&hash_fn).and_then(|f| match f.access { diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 2246a1d7..634435b8 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -141,7 +141,7 @@ macro_rules! reg_range { ($lib:ident | $x:expr => $( $y:ty ),*) => { $( $lib.set_iterator::>(); - let hash = $lib.set_fn_2($x, get_range::<$y>); + let hash = $lib.set_native_fn($x, get_range::<$y>); $lib.update_fn_metadata(hash, &[ concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y)), @@ -152,7 +152,7 @@ macro_rules! reg_range { ($lib:ident | step $x:expr => $( $y:ty ),*) => { $( $lib.set_iterator::>(); - let hash = $lib.set_fn_3($x, get_step_range::<$y>); + let hash = $lib.set_native_fn($x, get_step_range::<$y>); $lib.update_fn_metadata(hash, &[ concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y)), @@ -248,10 +248,10 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { lib.set_iterator::(); - let hash = lib.set_fn_2("range", |from, to| StepDecimalRange::new(from, to, Decimal::one())); + let hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one())); lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "Iterator"]); - let hash = lib.set_fn_3("range", |from, to, step| StepDecimalRange::new(from, to, step)); + let hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step)); lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator"]); } }); diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 8e8fe21a..e54763da 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -53,8 +53,7 @@ pub trait Package { /// Macro that makes it easy to define a _package_ (which is basically a shared [module][Module]) /// and register functions into it. /// -/// Functions can be added to the package using the standard module methods such as -/// [`set_fn_2`][Module::set_fn_2], [`set_fn_3_mut`][Module::set_fn_3_mut], [`set_fn_0`][Module::set_fn_0] etc. +/// Functions can be added to the package using [`Module::set_native_fn`]. /// /// # Example /// @@ -69,7 +68,7 @@ pub trait Package { /// def_package!(rhai:MyPackage:"My super-duper package", lib, /// { /// // Load a binary function with all value parameters. -/// lib.set_fn_2("my_add", add); +/// lib.set_native_fn("my_add", add); /// }); /// ``` #[macro_export] diff --git a/tests/for.rs b/tests/for.rs index 8827c123..65bca46f 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -171,7 +171,7 @@ fn test_for_module_iterator() -> Result<(), Box> { // Set a type iterator deep inside a nested module chain let mut sub_module = Module::new(); sub_module.set_iterable::(); - sub_module.set_fn_0("new_ts", || Ok(MyIterableType("hello".to_string()))); + sub_module.set_native_fn("new_ts", || Ok(MyIterableType("hello".to_string()))); let mut module = Module::new(); module.set_sub_module("inner", sub_module); diff --git a/tests/functions.rs b/tests/functions.rs index 7f8fe26c..ff71e390 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -75,7 +75,7 @@ fn test_functions_namespaces() -> Result<(), Box> { #[cfg(not(feature = "no_module"))] { let mut m = Module::new(); - let hash = m.set_fn_0("test", || Ok(999 as INT)); + let hash = m.set_native_fn("test", || Ok(999 as INT)); m.update_fn_namespace(hash, FnNamespace::Global); engine.register_static_module("hello", m.into()); diff --git a/tests/modules.rs b/tests/modules.rs index 934b430d..fe4d6175 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -23,11 +23,12 @@ fn test_module_sub_module() -> Result<(), Box> { let mut sub_module2 = Module::new(); sub_module2.set_var("answer", 41 as INT); - let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1)); + let hash_inc = sub_module2.set_native_fn("inc", |x: &mut INT| Ok(*x + 1)); sub_module2.build_index(); assert!(!sub_module2.contains_indexed_global_functions()); - sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1)); + let super_hash = sub_module2.set_native_fn("super_inc", |x: &mut INT| Ok(*x + 1)); + sub_module2.update_fn_namespace(super_hash, FnNamespace::Global); sub_module2.build_index(); assert!(sub_module2.contains_indexed_global_functions()); @@ -91,16 +92,16 @@ fn test_module_resolver() -> Result<(), Box> { let mut module = Module::new(); module.set_var("answer", 42 as INT); - module.set_fn_4("sum", |x: INT, y: INT, z: INT, w: INT| Ok(x + y + z + w)); - module.set_fn_1_mut("double", FnNamespace::Global, |x: &mut INT| { + module.set_native_fn("sum", |x: INT, y: INT, z: INT, w: INT| Ok(x + y + z + w)); + let double_hash = module.set_native_fn("double", |x: &mut INT| { *x *= 2; Ok(()) }); + module.update_fn_namespace(double_hash, FnNamespace::Global); #[cfg(not(feature = "no_float"))] - module.set_fn_4_mut( + module.set_native_fn( "sum_of_three_args", - FnNamespace::Internal, |target: &mut INT, a: INT, b: INT, c: rhai::FLOAT| { *target = a + b + c as INT; Ok(()) @@ -407,9 +408,9 @@ fn test_module_str() -> Result<(), Box> { let mut engine = rhai::Engine::new(); let mut module = Module::new(); - module.set_fn_1("test", test_fn); - module.set_fn_1("test2", test_fn2); - module.set_fn_1("test3", test_fn3); + module.set_native_fn("test", test_fn); + module.set_native_fn("test2", test_fn2); + module.set_native_fn("test3", test_fn3); let mut static_modules = rhai::module_resolvers::StaticModuleResolver::new(); static_modules.insert("test", module); From 12e3a5b050513f2a7e26a6c9d350368445c8633f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 15 Mar 2021 21:30:45 +0800 Subject: [PATCH 04/15] Add test of functions with context. --- tests/functions.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/functions.rs b/tests/functions.rs index ff71e390..73b4fc60 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "no_function"))] -use rhai::{Engine, EvalAltResult, FnNamespace, Module, ParseErrorType, INT}; +use rhai::{Engine, EvalAltResult, FnNamespace, Module, NativeCallContext, ParseErrorType, INT}; #[test] fn test_functions() -> Result<(), Box> { @@ -51,6 +51,20 @@ fn test_functions() -> Result<(), Box> { Ok(()) } +#[test] +fn test_functions_context() -> Result<(), Box> { + let mut engine = Engine::new(); + + engine.set_max_modules(40); + engine.register_fn("test", |context: NativeCallContext, x: INT| { + context.engine().max_modules() as INT + x + }); + + assert_eq!(engine.eval::("test(2)")?, 42); + + Ok(()) +} + #[test] fn test_functions_params() -> Result<(), Box> { let engine = Engine::new(); @@ -67,7 +81,6 @@ fn test_functions_params() -> Result<(), Box> { Ok(()) } -#[cfg(not(feature = "no_function"))] #[test] fn test_functions_namespaces() -> Result<(), Box> { let mut engine = Engine::new(); @@ -81,12 +94,16 @@ fn test_functions_namespaces() -> Result<(), Box> { engine.register_static_module("hello", m.into()); assert_eq!(engine.eval::("test()")?, 999); + + #[cfg(not(feature = "no_function"))] assert_eq!(engine.eval::("fn test() { 123 } test()")?, 123); } engine.register_fn("test", || 42 as INT); assert_eq!(engine.eval::("test()")?, 42); + + #[cfg(not(feature = "no_function"))] assert_eq!(engine.eval::("fn test() { 123 } test()")?, 123); Ok(()) From 6cbf771fdd3c181d7d6f0fd3eaf6044f207db006 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 15 Mar 2021 21:30:55 +0800 Subject: [PATCH 05/15] Fix private function signature. --- src/ast.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 8bb895df..22bb7426 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -74,7 +74,7 @@ impl fmt::Display for ScriptFnDef { "{}{}({})", match self.access { FnAccess::Public => "", - FnAccess::Private => "private", + FnAccess::Private => "private ", }, self.name, self.params @@ -118,7 +118,7 @@ impl fmt::Display for ScriptFnMetadata<'_> { "{}{}({})", match self.access { FnAccess::Public => "", - FnAccess::Private => "private", + FnAccess::Private => "private ", }, self.name, self.params.iter().cloned().collect::>().join(", ") From 27953ef74500085eeaf35f24c9b52935e9a1b41c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 15 Mar 2021 22:19:21 +0800 Subject: [PATCH 06/15] Register parameter type names. --- src/bin/rhai-repl.rs | 2 +- src/engine_api.rs | 32 +++++++++++++++++++++++++++----- src/fn_register.rs | 40 ++++++++++++++++++++++++++++++---------- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index fedd1dc0..8a03d85b 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -218,7 +218,7 @@ fn main() { // println!( // "{}", // engine - // .gen_fn_metadata_to_json(Some(&main_ast), false) + // .gen_fn_metadata_with_ast_to_json(&main_ast, true) // .unwrap() // ); // continue; diff --git a/src/engine_api.rs b/src/engine_api.rs index eb6b97f5..3ccf26bc 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -14,7 +14,7 @@ use crate::stdlib::{ }; use crate::{ scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext, - ParseError, Position, RhaiResult, Shared, AST, + ParseError, Position, RhaiResult, Shared, StaticVec, AST, }; #[cfg(not(feature = "no_index"))] @@ -51,16 +51,28 @@ impl Engine { /// # Ok(()) /// # } /// ``` + #[inline] pub fn register_fn(&mut self, name: &str, func: F) -> &mut Self where F: RegisterNativeFunction, { + let param_types = F::param_types(); + let mut param_type_names: StaticVec<_> = F::param_names() + .iter() + .map(|ty| format!("_: {}", self.map_type_name(ty))) + .collect(); + if F::return_type() != TypeId::of::<()>() { + param_type_names.push(self.map_type_name(F::return_type_name()).to_string()); + } + let param_type_names: StaticVec<_> = + param_type_names.iter().map(|ty| ty.as_str()).collect(); + self.global_namespace.set_fn( name, FnNamespace::Global, FnAccess::Public, - None, - &F::param_types(), + Some(¶m_type_names), + ¶m_types, func.into_callable_function(), ); self @@ -89,16 +101,26 @@ impl Engine { /// engine.eval::("div(42, 0)") /// .expect_err("expecting division by zero error!"); /// ``` + #[inline] pub fn register_result_fn(&mut self, name: &str, func: F) -> &mut Self where F: RegisterNativeFunction, { + let param_types = F::param_types(); + let mut param_type_names: StaticVec<_> = F::param_names() + .iter() + .map(|ty| format!("_: {}", self.map_type_name(ty))) + .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(); + self.global_namespace.set_fn( name, FnNamespace::Global, FnAccess::Public, - None, - &F::param_types(), + Some(¶m_type_names), + ¶m_types, func.into_callable_function(), ); self diff --git a/src/fn_register.rs b/src/fn_register.rs index f53157a0..8a773424 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -5,7 +5,13 @@ use crate::dynamic::{DynamicWriteLock, Variant}; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::r#unsafe::unsafe_try_cast; -use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String, vec}; +use crate::stdlib::{ + any::{type_name, 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 @@ -55,8 +61,14 @@ pub fn by_value(data: &mut Dynamic) -> T { /// Trait to register custom Rust functions. pub trait RegisterNativeFunction { - /// Get the types of this function's parameters. + /// Get the type ID's of this function's parameters. fn param_types() -> Box<[TypeId]>; + /// Get the type names of this function's parameters. + fn param_names() -> Box<[&'static str]>; + /// Get the type ID of this function's return value. + fn return_type() -> TypeId; + /// Get the type name of this function's return value. + fn return_type_name() -> &'static str; /// Convert this function into a [`CallableFunction`]. fn into_callable_function(self) -> CallableFunction; } @@ -78,8 +90,10 @@ macro_rules! def_register { $($par: Variant + Clone,)* 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_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::() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -101,8 +115,10 @@ macro_rules! def_register { $($par: Variant + Clone,)* 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_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::() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { @@ -124,8 +140,10 @@ macro_rules! def_register { $($par: Variant + Clone,)* 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_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::>>() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -144,8 +162,10 @@ macro_rules! def_register { $($par: Variant + Clone,)* 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_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::>>() } #[inline(always)] fn into_callable_function(self) -> CallableFunction { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { From a1549bb3772b3631db7e7d85e6fa06266062b3f1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 15 Mar 2021 22:37:50 +0800 Subject: [PATCH 07/15] Allow register_result to return any type. --- CHANGELOG.md | 5 +++++ src/engine_api.rs | 34 +++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9957e59e..9735f1b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ Breaking changes * `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it. * All `Module::set_fn_XXX` methods are removed, in favor of `Module::set_native_fn`. +Enhancements +------------ + +* `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any type. + Version 0.19.14 =============== diff --git a/src/engine_api.rs b/src/engine_api.rs index 3ccf26bc..7654ef74 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -82,15 +82,15 @@ impl Engine { /// # Example /// /// ``` - /// use rhai::{Engine, Dynamic, EvalAltResult}; + /// use rhai::{Engine, EvalAltResult}; /// /// // Normal function - /// fn div(x: i64, y: i64) -> Result> { + /// fn div(x: i64, y: i64) -> Result> { /// if y == 0 { /// // '.into()' automatically converts to 'Box' /// Err("division by zero!".into()) /// } else { - /// Ok((x / y).into()) + /// Ok(x / y) /// } /// } /// @@ -102,9 +102,9 @@ impl Engine { /// .expect_err("expecting division by zero error!"); /// ``` #[inline] - pub fn register_result_fn(&mut self, name: &str, func: F) -> &mut Self + pub fn register_result_fn(&mut self, name: &str, func: F) -> &mut Self where - F: RegisterNativeFunction, + F: RegisterNativeFunction>>, { let param_types = F::param_types(); let mut param_type_names: StaticVec<_> = F::param_names() @@ -321,8 +321,8 @@ impl Engine { /// fn new() -> Self { Self { field: 1 } } /// /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self) -> Result> { - /// Ok(self.field.into()) + /// fn get_field(&mut self) -> Result> { + /// Ok(self.field) /// } /// } /// @@ -342,10 +342,10 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn register_get_result( + pub fn register_get_result( &mut self, name: &str, - get_fn: impl Fn(&mut T) -> RhaiResult + SendSync + 'static, + get_fn: impl Fn(&mut T) -> Result> + SendSync + 'static, ) -> &mut Self { use crate::engine::make_getter; self.register_result_fn(&make_getter(name), get_fn) @@ -444,7 +444,7 @@ impl Engine { ) -> &mut Self { use crate::engine::make_setter; self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| { - set_fn(obj, value).map(Into::into) + set_fn(obj, value) }) } /// Short-hand for registering both getter and setter functions @@ -583,8 +583,8 @@ impl Engine { /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self, index: i64) -> Result> { - /// Ok(self.fields[index as usize].into()) + /// fn get_field(&mut self, index: i64) -> Result> { + /// Ok(self.fields[index as usize]) /// } /// } /// @@ -606,9 +606,13 @@ impl Engine { /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn register_indexer_get_result( + pub fn register_indexer_get_result< + T: Variant + Clone, + X: Variant + Clone, + U: Variant + Clone, + >( &mut self, - get_fn: impl Fn(&mut T, X) -> RhaiResult + SendSync + 'static, + get_fn: impl Fn(&mut T, X) -> Result> + SendSync + 'static, ) -> &mut Self { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); @@ -761,7 +765,7 @@ impl Engine { self.register_result_fn( crate::engine::FN_IDX_SET, - move |obj: &mut T, index: X, value: U| set_fn(obj, index, value).map(Into::into), + move |obj: &mut T, index: X, value: U| set_fn(obj, index, value), ) } /// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`]. From 082ecaa739d918a2a84ce8835a862006cf0e8e00 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 15 Mar 2021 23:03:26 +0800 Subject: [PATCH 08/15] Fix feature builds. --- src/engine_api.rs | 2 +- tests/functions.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine_api.rs b/src/engine_api.rs index 7654ef74..8f421688 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -9,7 +9,7 @@ use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, format, - string::String, + string::{String, ToString}, vec::Vec, }; use crate::{ diff --git a/tests/functions.rs b/tests/functions.rs index 73b4fc60..d5b8f190 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -51,6 +51,7 @@ fn test_functions() -> Result<(), Box> { Ok(()) } +#[cfg(not(feature = "no_module"))] #[test] fn test_functions_context() -> Result<(), Box> { let mut engine = Engine::new(); From 5172ade179b4523abe75787407f60bd74faf8f91 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 16 Mar 2021 18:16:40 +0800 Subject: [PATCH 09/15] Code style edits. --- src/engine.rs | 7 +- src/fn_call.rs | 181 ++++++++++++++++++------------------- src/fn_register.rs | 12 +-- src/packages/iter_basic.rs | 11 ++- 4 files changed, 101 insertions(+), 110 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 334a7c00..28b09837 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1133,7 +1133,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] EvalAltResult::ErrorIndexingType(_, _) => Some(new_val.unwrap()), // Any other error - return - err => return Err(Box::new(err)), + err => return err.into(), }, }; @@ -1986,10 +1986,11 @@ impl Engine { if lhs_ptr.as_ref().is_read_only() { // Assignment to constant variable - Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( + EvalAltResult::ErrorAssignmentToConstant( lhs_expr.get_variable_access(false).unwrap().to_string(), pos, - ))) + ) + .into() } else { self.eval_op_assignment( mods, diff --git a/src/fn_call.rs b/src/fn_call.rs index f4c3c0e6..63e8969b 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -33,28 +33,6 @@ use crate::{ #[cfg(not(feature = "no_object"))] use crate::Map; -/// Extract the property name from a getter function name. -#[cfg(not(feature = "no_object"))] -#[inline(always)] -fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> { - if _fn_name.starts_with(crate::engine::FN_GET) { - Some(&_fn_name[crate::engine::FN_GET.len()..]) - } else { - None - } -} - -/// Extract the property name from a setter function name. -#[cfg(not(feature = "no_object"))] -#[inline(always)] -fn extract_prop_from_setter(_fn_name: &str) -> Option<&str> { - if _fn_name.starts_with(crate::engine::FN_SET) { - Some(&_fn_name[crate::engine::FN_SET.len()..]) - } else { - None - } -} - /// A type that temporarily stores a mutable reference to a `Dynamic`, /// replacing it with a cloned copy. #[derive(Debug, Default)] @@ -407,69 +385,79 @@ impl Engine { }); } - // Getter function not found? - #[cfg(not(feature = "no_object"))] - if let Some(prop) = extract_prop_from_getter(fn_name) { - return EvalAltResult::ErrorDotExpr( - format!( - "Unknown property '{}' - a getter is not registered for type '{}'", - prop, - self.map_type_name(args[0].type_name()) - ), + match fn_name { + // index getter function not found? + #[cfg(not(feature = "no_index"))] + crate::engine::FN_IDX_GET => { + assert!(args.len() == 2); + + EvalAltResult::ErrorFunctionNotFound( + format!( + "{} [{}]", + self.map_type_name(args[0].type_name()), + self.map_type_name(args[1].type_name()), + ), + pos, + ) + .into() + } + + // index setter function not found? + #[cfg(not(feature = "no_index"))] + crate::engine::FN_IDX_SET => { + assert!(args.len() == 3); + + EvalAltResult::ErrorFunctionNotFound( + format!( + "{} [{}]=", + self.map_type_name(args[0].type_name()), + self.map_type_name(args[1].type_name()), + ), + pos, + ) + .into() + } + + // Getter function not found? + #[cfg(not(feature = "no_object"))] + _ if fn_name.starts_with(crate::engine::FN_GET) => { + assert!(args.len() == 1); + + EvalAltResult::ErrorDotExpr( + format!( + "Unknown property '{}' - a getter is not registered for type '{}'", + &fn_name[crate::engine::FN_GET.len()..], + self.map_type_name(args[0].type_name()) + ), + pos, + ) + .into() + } + + // Setter function not found? + #[cfg(not(feature = "no_object"))] + _ if fn_name.starts_with(crate::engine::FN_SET) => { + assert!(args.len() == 2); + + EvalAltResult::ErrorDotExpr( + format!( + "No writable property '{}' - a setter is not registered for type '{}' to handle '{}'", + &fn_name[crate::engine::FN_SET.len()..], + self.map_type_name(args[0].type_name()), + self.map_type_name(args[1].type_name()), + ), + pos, + ) + .into() + } + + // Raise error + _ => EvalAltResult::ErrorFunctionNotFound( + self.gen_call_signature(None, fn_name, args.as_ref()), pos, ) - .into(); + .into(), } - - // Setter function not found? - #[cfg(not(feature = "no_object"))] - if let Some(prop) = extract_prop_from_setter(fn_name) { - return EvalAltResult::ErrorDotExpr( - format!( - "No writable property '{}' - a setter is not registered for type '{}' to handle '{}'", - prop, - self.map_type_name(args[0].type_name()), - self.map_type_name(args[1].type_name()), - ), - pos, - ) - .into(); - } - - // index getter function not found? - #[cfg(not(feature = "no_index"))] - if fn_name == crate::engine::FN_IDX_GET && args.len() == 2 { - return EvalAltResult::ErrorFunctionNotFound( - format!( - "{} [{}]", - self.map_type_name(args[0].type_name()), - self.map_type_name(args[1].type_name()), - ), - pos, - ) - .into(); - } - - // index setter function not found? - #[cfg(not(feature = "no_index"))] - if fn_name == crate::engine::FN_IDX_SET { - return EvalAltResult::ErrorFunctionNotFound( - format!( - "{} [{}]=", - self.map_type_name(args[0].type_name()), - self.map_type_name(args[1].type_name()), - ), - pos, - ) - .into(); - } - - // Raise error - EvalAltResult::ErrorFunctionNotFound( - self.gen_call_signature(None, fn_name, args.as_ref()), - pos, - ) - .into() } /// Call a script-defined function. @@ -500,7 +488,7 @@ impl Engine { err: Box, pos: Position, ) -> RhaiResult { - Err(Box::new(EvalAltResult::ErrorInFunctionCall( + EvalAltResult::ErrorInFunctionCall( name, fn_def .lib @@ -510,7 +498,8 @@ impl Engine { .to_string(), err, pos, - ))) + ) + .into() } self.inc_operations(state, pos)?; @@ -523,7 +512,7 @@ impl Engine { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "unchecked"))] if level > self.max_call_levels() { - return Err(Box::new(EvalAltResult::ErrorStackOverflow(pos))); + return EvalAltResult::ErrorStackOverflow(pos).into(); } let orig_scope_level = state.scope_level; @@ -586,10 +575,10 @@ impl Engine { make_error(fn_name, fn_def, state, err, pos) } // System errors are passed straight-through - mut err if err.is_system_exception() => Err(Box::new({ + mut err if err.is_system_exception() => { err.set_position(pos); - err - })), + err.into() + } // Other errors are wrapped in `ErrorInFunctionCall` _ => make_error(fn_def.name.to_string(), fn_def, state, err, pos), }); @@ -699,36 +688,39 @@ impl Engine { // Handle is_shared() #[cfg(not(feature = "no_closure"))] crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => { - return Err(Box::new(EvalAltResult::ErrorRuntime( + return EvalAltResult::ErrorRuntime( format!( "'{}' should not be called this way. Try {}(...);", fn_name, fn_name ) .into(), pos, - ))) + ) + .into() } KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if args.len() == 1 => { - return Err(Box::new(EvalAltResult::ErrorRuntime( + return EvalAltResult::ErrorRuntime( format!( "'{}' should not be called this way. Try {}(...);", fn_name, fn_name ) .into(), pos, - ))) + ) + .into() } KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !args.is_empty() => { - return Err(Box::new(EvalAltResult::ErrorRuntime( + return EvalAltResult::ErrorRuntime( format!( "'{}' should not be called this way. Try {}(...);", fn_name, fn_name ) .into(), pos, - ))) + ) + .into() } _ => (), @@ -865,7 +857,6 @@ impl Engine { } /// Evaluate a text script in place - used primarily for 'eval'. - #[inline] fn eval_script_expr_in_place( &self, scope: &mut Scope, diff --git a/src/fn_register.rs b/src/fn_register.rs index 8a773424..7d04bdb2 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -94,8 +94,7 @@ macro_rules! def_register { #[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::() } - #[inline(always)] - fn into_callable_function(self) -> CallableFunction { + #[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! let mut _drain = args.iter_mut(); @@ -119,8 +118,7 @@ macro_rules! def_register { #[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::() } - #[inline(always)] - fn into_callable_function(self) -> CallableFunction { + #[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! let mut _drain = args.iter_mut(); @@ -144,8 +142,7 @@ macro_rules! def_register { #[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::>>() } - #[inline(always)] - fn into_callable_function(self) -> CallableFunction { + #[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! let mut _drain = args.iter_mut(); @@ -166,8 +163,7 @@ macro_rules! def_register { #[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::>>() } - #[inline(always)] - fn into_callable_function(self) -> CallableFunction { + #[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! let mut _drain = args.iter_mut(); diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 634435b8..5eae23c6 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -29,7 +29,7 @@ where #[cfg(not(feature = "unchecked"))] if let Some(r) = from.checked_add(&step) { if r == from { - return Err(Box::new(EvalAltResult::ErrorInFunctionCall( + return EvalAltResult::ErrorInFunctionCall( "range".to_string(), "".to_string(), Box::new(EvalAltResult::ErrorArithmetic( @@ -37,7 +37,8 @@ where crate::Position::NONE, )), crate::Position::NONE, - ))); + ) + .into(); } } @@ -204,10 +205,10 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { if step.is_zero() { use crate::stdlib::string::ToString; - return Err(Box::new(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), + return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)), crate::Position::NONE, - ))); + ).into(); } Ok(Self(from, to, step)) @@ -246,6 +247,8 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { } } + impl crate::stdlib::iter::FusedIterator for StepDecimalRange {} + lib.set_iterator::(); let hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one())); From 4581943435c3f607ef4ee1ca9863ca77daee974e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Mar 2021 09:58:08 +0800 Subject: [PATCH 10/15] Remove public_only parameters. --- CHANGELOG.md | 5 +++ src/engine_api.rs | 2 +- src/fn_call.rs | 95 ++++++++++++++++++++--------------------------- src/module/mod.rs | 30 +++------------ src/optimize.rs | 6 +-- tests/modules.rs | 2 +- 6 files changed, 56 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9735f1b4..a39d959e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -575,6 +575,11 @@ Bug fixes * Fixes bug that prevents calling functions in closures. * Fixes bug that erroneously consumes the first argument to a namespace-qualified function call. +Breaking changes +---------------- + +* `Module::contains_fn` and `Module::get_script_fn` no longer take the `public_only` parameter. + New features ------------ diff --git a/src/engine_api.rs b/src/engine_api.rs index 8f421688..6900a268 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1881,7 +1881,7 @@ impl Engine { let fn_def = ast .lib() - .get_script_fn(name, args.len(), false) + .get_script_fn(name, args.len()) .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; // Check for data race. diff --git a/src/fn_call.rs b/src/fn_call.rs index 63e8969b..2f683808 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -124,6 +124,7 @@ pub fn ensure_no_data_race( impl Engine { /// Generate the signature for a function call. + #[inline] fn gen_call_signature( &self, namespace: Option<&NamespaceRef>, @@ -176,7 +177,7 @@ impl Engine { .fn_resolution_cache_mut() .entry(hash) .or_insert_with(|| { - let num_args = args.as_ref().map(|a| a.len()).unwrap_or(0); + let num_args = args.as_ref().map_or(0, |a| a.len()); let max_bitmask = if !allow_dynamic { 0 } else { @@ -188,43 +189,37 @@ impl Engine { let func = lib .iter() .find_map(|m| { - m.get_fn(hash, false) - .cloned() - .map(|func| FnResolutionCacheEntry { - func, - source: m.id_raw().cloned(), - }) + m.get_fn(hash).cloned().map(|func| { + let source = m.id_raw().cloned(); + FnResolutionCacheEntry { func, source } + }) }) .or_else(|| { self.global_namespace - .get_fn(hash, false) + .get_fn(hash) .cloned() .map(|func| FnResolutionCacheEntry { func, source: None }) }) .or_else(|| { self.global_modules.iter().find_map(|m| { - m.get_fn(hash, false) - .cloned() - .map(|func| FnResolutionCacheEntry { - func, - source: m.id_raw().cloned(), - }) + m.get_fn(hash).cloned().map(|func| { + let source = m.id_raw().cloned(); + FnResolutionCacheEntry { func, source } + }) }) }) .or_else(|| { - mods.get_fn(hash) - .map(|(func, source)| FnResolutionCacheEntry { - func: func.clone(), - source: source.cloned(), - }) + mods.get_fn(hash).map(|(func, source)| { + let func = func.clone(); + let source = source.cloned(); + FnResolutionCacheEntry { func, source } + }) }) .or_else(|| { self.global_sub_modules.values().find_map(|m| { m.get_qualified_fn(hash).cloned().map(|func| { - FnResolutionCacheEntry { - func, - source: m.id_raw().cloned(), - } + let source = m.id_raw().cloned(); + FnResolutionCacheEntry { func, source } }) }) }); @@ -235,41 +230,31 @@ impl Engine { // Stop when all permutations are exhausted None if bitmask >= max_bitmask => { - return if num_args != 2 { - None - } else if let Some(ref args) = args { + if num_args != 2 { + return None; + } + + return args.and_then(|args| { if !is_op_assignment { - if let Some(f) = - get_builtin_binary_op_fn(fn_name, &args[0], &args[1]) - { - Some(FnResolutionCacheEntry { - func: CallableFunction::from_method( - Box::new(f) as Box - ), - source: None, - }) - } else { - None - } + get_builtin_binary_op_fn(fn_name, &args[0], &args[1]).map(|f| { + let func = CallableFunction::from_method( + Box::new(f) as Box + ); + FnResolutionCacheEntry { func, source: None } + }) } else { let (first, second) = args.split_first().unwrap(); - if let Some(f) = - get_builtin_op_assignment_fn(fn_name, *first, second[0]) - { - Some(FnResolutionCacheEntry { - func: CallableFunction::from_method( + get_builtin_op_assignment_fn(fn_name, *first, second[0]).map( + |f| { + let func = CallableFunction::from_method( Box::new(f) as Box - ), - source: None, - }) - } else { - None - } + ); + FnResolutionCacheEntry { func, source: None } + }, + ) } - } else { - None - } + }); } // Try all permutations with `Dynamic` wildcards @@ -612,11 +597,11 @@ impl Engine { } // First check script-defined functions - let result = lib.iter().any(|&m| m.contains_fn(hash_script, false)) + let result = lib.iter().any(|&m| m.contains_fn(hash_script)) // Then check registered functions - || self.global_namespace.contains_fn(hash_script, false) + || self.global_namespace.contains_fn(hash_script) // Then check packages - || self.global_modules.iter().any(|m| m.contains_fn(hash_script, false)) + || self.global_modules.iter().any(|m| m.contains_fn(hash_script)) // Then check imported modules || mods.map_or(false, |m| m.contains_fn(hash_script)) // Then check sub-modules diff --git a/src/module/mod.rs b/src/module/mod.rs index 73e6aeea..626a5e65 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -513,15 +513,10 @@ impl Module { &self, name: &str, num_params: usize, - public_only: bool, ) -> Option<&Shared> { self.functions .values() - .find(|f| { - (!public_only || f.access == FnAccess::Public) - && f.params == num_params - && f.name == name - }) + .find(|f| f.params == num_params && f.name == name) .map(|f| f.func.get_fn_def()) } @@ -616,20 +611,11 @@ impl Module { /// /// let mut module = Module::new(); /// let hash = module.set_native_fn("calc", || Ok(42_i64)); - /// assert!(module.contains_fn(hash, true)); + /// assert!(module.contains_fn(hash)); /// ``` #[inline(always)] - pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool { - if public_only { - self.functions - .get(&hash_fn) - .map_or(false, |f| match f.access { - FnAccess::Public => true, - FnAccess::Private => false, - }) - } else { - self.functions.contains_key(&hash_fn) - } + pub fn contains_fn(&self, hash_fn: u64) -> bool { + self.functions.contains_key(&hash_fn) } /// Update the metadata (parameter names/types and return type) of a registered function. @@ -1110,12 +1096,8 @@ impl Module { /// /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. #[inline(always)] - pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> { - self.functions.get(&hash_fn).and_then(|f| match f.access { - _ if !public_only => Some(&f.func), - FnAccess::Public => Some(&f.func), - FnAccess::Private => None, - }) + pub(crate) fn get_fn(&self, hash_fn: u64) -> Option<&CallableFunction> { + self.functions.get(&hash_fn).map(|f| &f.func) } /// Does the particular namespace-qualified function exist in the [`Module`]? diff --git a/src/optimize.rs b/src/optimize.rs index 3cf81224..37ac11ae 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -139,9 +139,9 @@ fn has_native_fn(state: &State, hash_script: u64, arg_types: &[TypeId]) -> bool let hash = combine_hashes(hash_script, hash_params); // First check registered functions - state.engine.global_namespace.contains_fn(hash, false) + state.engine.global_namespace.contains_fn(hash) // Then check packages - || state.engine.global_modules.iter().any(|m| m.contains_fn(hash, false)) + || state.engine.global_modules.iter().any(|m| m.contains_fn(hash)) // Then check sub-modules || state.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)) } @@ -774,7 +774,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { => { // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] - let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.args.len(), false).is_some()); + let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.args.len()).is_some()); #[cfg(feature = "no_function")] let has_script_fn = false; diff --git a/tests/modules.rs b/tests/modules.rs index fe4d6175..64401696 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -49,7 +49,7 @@ fn test_module_sub_module() -> Result<(), Box> { let m2 = m.get_sub_module("universe").unwrap(); assert!(m2.contains_var("answer")); - assert!(m2.contains_fn(hash_inc, false)); + assert!(m2.contains_fn(hash_inc)); assert_eq!(m2.get_var_value::("answer").unwrap(), 41); From 699131be58a6045f12ac310c3766744286afe6da Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Mar 2021 10:31:50 +0800 Subject: [PATCH 11/15] New reserved keywords. --- CHANGELOG.md | 1 + src/token.rs | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a39d959e..0d0dd3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Breaking changes * The traits `RegisterFn` and `RegisterResultFn` are removed. `Engine::register_fn` and `Engine::register_result_fn` are now implemented directly on `Engine`. * `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it. * All `Module::set_fn_XXX` methods are removed, in favor of `Module::set_native_fn`. +* `protected`, `super` are now reserved keywords. Enhancements ------------ diff --git a/src/token.rs b/src/token.rs index 909442c3..4c697381 100644 --- a/src/token.rs +++ b/src/token.rs @@ -576,10 +576,10 @@ impl Token { "import" | "export" | "as" => Reserved(syntax.into()), "===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "public" - | "new" | "use" | "module" | "package" | "var" | "static" | "begin" | "end" - | "shared" | "with" | "each" | "then" | "goto" | "unless" | "exit" | "match" - | "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" - | "async" | "await" | "yield" => Reserved(syntax.into()), + | "protected" | "super" | "new" | "use" | "module" | "package" | "var" | "static" + | "begin" | "end" | "shared" | "with" | "each" | "then" | "goto" | "unless" + | "exit" | "match" | "case" | "default" | "void" | "null" | "nil" | "spawn" + | "thread" | "go" | "sync" | "async" | "await" | "yield" => Reserved(syntax.into()), KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => { From 6667a22c0c278756ccbba213aa591c51ad5b8199 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Mar 2021 13:30:47 +0800 Subject: [PATCH 12/15] Code style and docs. --- src/ast.rs | 49 ++++++++++++++++++++------- src/engine.rs | 39 ++++++++++----------- src/fn_call.rs | 18 +++++----- src/fn_native.rs | 6 ++-- src/lib.rs | 71 +++++++++++++++++++++++++++++++++++---- src/module/mod.rs | 12 ++++--- src/packages/fn_basic.rs | 4 +-- src/packages/map_basic.rs | 8 ++--- src/parser.rs | 34 +++++++++---------- src/scope.rs | 4 +-- src/serde/metadata.rs | 10 +++--- src/utils.rs | 20 +++++------ 12 files changed, 172 insertions(+), 103 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 22bb7426..a3d143ae 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1301,18 +1301,41 @@ pub struct OpAssignment { /// _(INTERNALS)_ An set of function call hashes. /// Exported under the `internals` feature only. /// +/// Two separate hashes are pre-calculated because of the following pattern: +/// +/// ```rust,no_run +/// func(a, b, c); // Native: func(a, b, c) - 3 parameters +/// // Script: func(a, b, c) - 3 parameters +/// +/// a.func(b, c); // Native: func(&mut a, b, c) - 3 parameters +/// // Script: func(b, c) - 2 parameters +/// ``` +/// +/// For normal function calls, the native hash equals the script hash. +/// For method-style calls, the script hash contains one fewer parameter. +/// +/// Function call hashes are used in the following manner: +/// +/// * First, the script hash is tried, which contains only the called function's name plus the +/// of parameters. +/// +/// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is +/// then used to search for a native function. +/// In other words, a native function call hash always contains the called function's name plus +/// the types of the arguments. This is to due to possible function overloading for different parameter types. +/// /// # Volatile Data Structure /// /// This type is volatile and may change. #[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] -pub struct FnHash { +pub struct FnCallHash { /// Pre-calculated hash for a script-defined function ([`None`] if native functions only). - script: Option, + pub script: Option, /// Pre-calculated hash for a native Rust function with no parameter types. - native: u64, + pub native: u64, } -impl fmt::Debug for FnHash { +impl fmt::Debug for FnCallHash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(script) = self.script { if script == self.native { @@ -1326,8 +1349,8 @@ impl fmt::Debug for FnHash { } } -impl FnHash { - /// Create a [`FnHash`] with only the native Rust hash. +impl FnCallHash { + /// Create a [`FnCallHash`] with only the native Rust hash. #[inline(always)] pub fn from_native(hash: u64) -> Self { Self { @@ -1335,7 +1358,7 @@ impl FnHash { native: hash, } } - /// Create a [`FnHash`] with both native Rust and script function hashes set to the same value. + /// Create a [`FnCallHash`] with both native Rust and script function hashes set to the same value. #[inline(always)] pub fn from_script(hash: u64) -> Self { Self { @@ -1343,7 +1366,7 @@ impl FnHash { native: hash, } } - /// Create a [`FnHash`] with both native Rust and script function hashes. + /// Create a [`FnCallHash`] with both native Rust and script function hashes. #[inline(always)] pub fn from_script_and_native(script: u64, native: u64) -> Self { Self { @@ -1351,21 +1374,21 @@ impl FnHash { native, } } - /// Is this [`FnHash`] native Rust only? + /// Is this [`FnCallHash`] native Rust only? #[inline(always)] pub fn is_native_only(&self) -> bool { self.script.is_none() } - /// Get the script function hash from this [`FnHash`]. + /// Get the script function hash from this [`FnCallHash`]. /// /// # Panics /// - /// Panics if the [`FnHash`] is native Rust only. + /// Panics if the [`FnCallHash`] is native Rust only. #[inline(always)] pub fn script_hash(&self) -> u64 { self.script.unwrap() } - /// Get the naive Rust function hash from this [`FnHash`]. + /// Get the naive Rust function hash from this [`FnCallHash`]. #[inline(always)] pub fn native_hash(&self) -> u64 { self.native @@ -1381,7 +1404,7 @@ impl FnHash { #[derive(Debug, Clone, Default, Hash)] pub struct FnCallExpr { /// Pre-calculated hash. - pub hash: FnHash, + pub hash: FnCallHash, /// Does this function call capture the parent scope? pub capture: bool, /// List of function call arguments. diff --git a/src/engine.rs b/src/engine.rs index 28b09837..4061431c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation [`Engine`]. -use crate::ast::{Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, Stmt, StmtBlock}; +use crate::ast::{Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType, Stmt, StmtBlock}; use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::fn_native::{ CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback, @@ -527,10 +527,8 @@ pub struct State { /// Embedded module resolver. #[cfg(not(feature = "no_module"))] pub resolver: Option>, - /// function resolution cache. - fn_resolution_caches: StaticVec, - /// Free resolution caches. - fn_resolution_caches_free_list: Vec, + /// Function resolution cache and free list. + fn_resolution_caches: (StaticVec, Vec), } impl State { @@ -541,20 +539,19 @@ impl State { } /// Get a mutable reference to the current function resolution cache. pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { - if self.fn_resolution_caches.is_empty() { + if self.fn_resolution_caches.0.is_empty() { self.fn_resolution_caches - .push(HashMap::with_capacity_and_hasher(16, StraightHasherBuilder)); + .0 + .push(HashMap::with_capacity_and_hasher(64, StraightHasherBuilder)); } - self.fn_resolution_caches.last_mut().unwrap() + self.fn_resolution_caches.0.last_mut().unwrap() } /// Push an empty function resolution cache onto the stack and make it current. #[allow(dead_code)] pub fn push_fn_resolution_cache(&mut self) { - self.fn_resolution_caches.push( - self.fn_resolution_caches_free_list - .pop() - .unwrap_or_default(), - ); + self.fn_resolution_caches + .0 + .push(self.fn_resolution_caches.1.pop().unwrap_or_default()); } /// Remove the current function resolution cache from the stack and make the last one current. /// @@ -562,9 +559,9 @@ impl State { /// /// Panics if there are no more function resolution cache in the stack. pub fn pop_fn_resolution_cache(&mut self) { - let mut cache = self.fn_resolution_caches.pop().unwrap(); + let mut cache = self.fn_resolution_caches.0.pop().unwrap(); cache.clear(); - self.fn_resolution_caches_free_list.push(cache); + self.fn_resolution_caches.1.push(cache); } } @@ -1143,7 +1140,7 @@ impl Engine { let ((_, val_pos), _) = new_val; let hash_set = - FnHash::from_native(calc_fn_hash(empty(), FN_IDX_SET, 3)); + FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_SET, 3)); let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0]; self.exec_fn_call( @@ -1221,7 +1218,7 @@ impl Engine { // xxx.id = ??? Expr::Property(x) if new_val.is_some() => { let (_, (setter, hash_set), Ident { pos, .. }) = x.as_ref(); - let hash = FnHash::from_native(*hash_set); + let hash = FnCallHash::from_native(*hash_set); let mut new_val = new_val; let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0]; self.exec_fn_call( @@ -1233,7 +1230,7 @@ impl Engine { // xxx.id Expr::Property(x) => { let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref(); - let hash = FnHash::from_native(*hash_get); + let hash = FnCallHash::from_native(*hash_get); let mut args = [target_val]; self.exec_fn_call( mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None, @@ -1282,8 +1279,8 @@ impl Engine { Expr::Property(p) => { let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = p.as_ref(); - let hash_get = FnHash::from_native(*hash_get); - let hash_set = FnHash::from_native(*hash_set); + let hash_get = FnCallHash::from_native(*hash_get); + let hash_set = FnCallHash::from_native(*hash_set); let arg_values = &mut [target_val, &mut Default::default()]; let args = &mut arg_values[..1]; let (mut val, updated) = self.exec_fn_call( @@ -1615,7 +1612,7 @@ impl Engine { let type_name = target.type_name(); let mut idx = idx; let args = &mut [target, &mut idx]; - let hash_get = FnHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2)); + let hash_get = FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2)); self.exec_fn_call( _mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None, _level, diff --git a/src/fn_call.rs b/src/fn_call.rs index 2f683808..c07e5c13 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1,6 +1,6 @@ //! Implement function-calling mechanism for [`Engine`]. -use crate::ast::FnHash; +use crate::ast::FnCallHash; use crate::engine::{ FnResolutionCacheEntry, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, @@ -627,7 +627,7 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - hash: FnHash, + hash: FnCallHash, args: &mut FnCallArgs, is_ref: bool, _is_method: bool, @@ -893,7 +893,7 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - mut hash: FnHash, + mut hash: FnCallHash, target: &mut crate::engine::Target, (call_args, call_arg_positions): &mut (StaticVec, StaticVec), pos: Position, @@ -913,7 +913,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hashes - let new_hash = FnHash::from_script(calc_fn_hash(empty(), fn_name, args_len)); + let new_hash = FnCallHash::from_script(calc_fn_hash(empty(), fn_name, args_len)); // Arguments are passed as-is, adding the curried arguments let mut curry = fn_ptr.curry().iter().cloned().collect::>(); let mut arg_values = curry @@ -949,7 +949,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hash - let new_hash = FnHash::from_script_and_native( + let new_hash = FnCallHash::from_script_and_native( calc_fn_hash(empty(), fn_name, args_len), calc_fn_hash(empty(), fn_name, args_len + 1), ); @@ -1024,7 +1024,7 @@ impl Engine { call_arg_positions.insert(i, Position::NONE); }); // Recalculate the hash based on the new function name and new arguments - hash = FnHash::from_script_and_native( + hash = FnCallHash::from_script_and_native( calc_fn_hash(empty(), fn_name, call_args.len()), calc_fn_hash(empty(), fn_name, call_args.len() + 1), ); @@ -1062,7 +1062,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, args_expr: &[Expr], - mut hash: FnHash, + mut hash: FnCallHash, pos: Position, capture_scope: bool, level: usize, @@ -1101,9 +1101,9 @@ impl Engine { // Recalculate hash let args_len = args_expr.len() + curry.len(); hash = if !hash.is_native_only() { - FnHash::from_script(calc_fn_hash(empty(), name, args_len)) + FnCallHash::from_script(calc_fn_hash(empty(), name, args_len)) } else { - FnHash::from_native(calc_fn_hash(empty(), name, args_len)) + FnCallHash::from_native(calc_fn_hash(empty(), name, args_len)) }; } // Handle Fn() diff --git a/src/fn_native.rs b/src/fn_native.rs index f4d41ae4..12cb94b6 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,6 +1,6 @@ //! Module defining interfaces to native-Rust functions. -use crate::ast::{FnAccess, FnHash}; +use crate::ast::{FnAccess, FnCallHash}; use crate::engine::Imports; use crate::plugin::PluginFunction; use crate::stdlib::{ @@ -190,12 +190,12 @@ impl<'a> NativeCallContext<'a> { args: &mut [&mut Dynamic], ) -> RhaiResult { let hash = if is_method { - FnHash::from_script_and_native( + FnCallHash::from_script_and_native( calc_fn_hash(empty(), fn_name, args.len() - 1), calc_fn_hash(empty(), fn_name, args.len()), ) } else { - FnHash::from_script(calc_fn_hash(empty(), fn_name, args.len())) + FnCallHash::from_script(calc_fn_hash(empty(), fn_name, args.len())) }; self.engine() diff --git a/src/lib.rs b/src/lib.rs index 215db356..0b34b4af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,8 +191,8 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni #[cfg(feature = "internals")] #[deprecated = "this type is volatile and may change"] pub use ast::{ - ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnHash, Ident, OpAssignment, - ReturnType, ScriptFnDef, Stmt, StmtBlock, + ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHash, Ident, + OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock, }; #[cfg(feature = "internals")] @@ -207,15 +207,72 @@ pub use engine::Limits; #[deprecated = "this type is volatile and may change"] pub use module::NamespaceRef; -/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), -/// which is a specialized [`Vec`] backed by a small, fixed-size array when there are <= 4 items stored. -/// Exported under the `internals` feature only. +/// Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), which is a +/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are <= 4 items stored. +/// +/// # Background +/// +/// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec` +/// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy." +/// +/// And the Lord did grin, and the people did feast upon the lambs and sloths and carp and anchovies +/// and orangutans and breakfast cereals and fruit bats and large chu... +/// +/// And the Lord spake, saying, "First shalt thou depend on the [`smallvec`](https://crates.io/crates/smallvec) crate. +/// Then, shalt thou keep four inline. No more. No less. Four shalt be the number thou shalt keep inline, +/// and the number to keep inline shalt be four. Five shalt thou not keep inline, nor either keep inline +/// thou two or three, excepting that thou then proceed to four. Six is right out. Once the number four, +/// being the forth number, be reached, then, lobbest thou thy `SmallVec` towards thy heap, who, +/// being slow and cache-naughty in My sight, shall snuff it." +/// +/// # Explanation on the Number Four +/// +/// `StaticVec` is used frequently to keep small lists of items in inline (non-heap) storage in +/// order to improve cache friendliness and reduce indirections. +/// +/// The number 4, other than being the holy number, is carefully chosen for a balance between +/// storage space and reduce allocations. That is because most function calls (and most functions, +/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a +/// large number of external variables. +/// +/// In addition, most scripts blocks either contain many statements, or just a few lines; +/// most scripts load fewer than 5 external modules; most module paths contain fewer than 5 levels +/// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long). #[cfg(not(feature = "internals"))] type StaticVec = smallvec::SmallVec<[T; 4]>; -/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), -/// which is a specialized [`Vec`] backed by a small, fixed-size array when there are <= 4 items stored. +/// _(INTERNALS)_ Alias to [`smallvec`](https://crates.io/crates/smallvec), which is a specialized +/// [`Vec`] backed by a small, inline, fixed-size array when there are <= 4 items stored. /// Exported under the `internals` feature only. +/// +/// # Background +/// +/// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec` +/// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy." +/// +/// And the Lord did grin, and the people did feast upon the lambs and sloths and carp and anchovies +/// and orangutans and breakfast cereals and fruit bats and large chu... +/// +/// And the Lord spake, saying, "First shalt thou depend on the [`smallvec`](https://crates.io/crates/smallvec) crate. +/// Then, shalt thou keep four inline. No more. No less. Four shalt be the number thou shalt keep inline, +/// and the number to keep inline shalt be four. Five shalt thou not keep inline, nor either keep inline +/// thou two or three, excepting that thou then proceed to four. Six is right out. Once the number four, +/// being the forth number, be reached, then, lobbest thou thy `SmallVec` towards thy heap, who, +/// being slow and cache-naughty in My sight, shall snuff it." +/// +/// # Explanation on the Number Four +/// +/// `StaticVec` is used frequently to keep small lists of items in inline (non-heap) storage in +/// order to improve cache friendliness and reduce indirections. +/// +/// The number 4, other than being the holy number, is carefully chosen for a balance between +/// storage space and reduce allocations. That is because most function calls (and most functions, +/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a +/// large number of external variables. +/// +/// In addition, most scripts blocks either contain many statements, or just a few lines; +/// most scripts load fewer than 5 external modules; most module paths contain fewer than 5 levels +/// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long). #[cfg(feature = "internals")] pub type StaticVec = smallvec::SmallVec<[T; 4]>; diff --git a/src/module/mod.rs b/src/module/mod.rs index 626a5e65..2ca01980 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1398,12 +1398,14 @@ impl Module { scope.into_iter().for_each(|(_, value, mut aliases)| { // Variables with an alias left in the scope become module variables - if aliases.len() > 1 { - aliases.into_iter().for_each(|alias| { + match aliases.len() { + 0 => (), + 1 => { + module.variables.insert(aliases.pop().unwrap(), value); + } + _ => aliases.into_iter().for_each(|alias| { module.variables.insert(alias, value.clone()); - }); - } else if aliases.len() == 1 { - module.variables.insert(aliases.pop().unwrap(), value); + }), } }); diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 3a6e489d..c6eab910 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -108,8 +108,8 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { list.push(make_metadata(dict, Some(namespace.clone()), f).into()) }); module.iter_sub_modules().for_each(|(ns, m)| { - let ns: ImmutableString = format!("{}::{}", namespace, ns).into(); - scan_module(list, dict, ns, m.as_ref()) + let ns = format!("{}::{}", namespace, ns); + scan_module(list, dict, ns.into(), m.as_ref()) }); } diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index f54af86c..673b9be3 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -29,15 +29,11 @@ mod map_functions { } #[rhai_fn(name = "mixin", name = "+=")] pub fn mixin(map: &mut Map, map2: Map) { - map2.into_iter().for_each(|(key, value)| { - map.insert(key, value); - }); + map.extend(map2.into_iter()); } #[rhai_fn(name = "+")] pub fn merge(mut map: Map, map2: Map) -> Map { - map2.into_iter().for_each(|(key, value)| { - map.insert(key, value); - }); + map.extend(map2.into_iter()); map } pub fn fill_with(map: &mut Map, map2: Map) { diff --git a/src/parser.rs b/src/parser.rs index 12a714a8..21ae4114 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,8 +1,8 @@ //! Main module defining the lexer and parser. use crate::ast::{ - BinaryExpr, CustomExpr, Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, ScriptFnDef, - Stmt, StmtBlock, + BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType, + ScriptFnDef, Stmt, StmtBlock, }; use crate::dynamic::{AccessMode, Union}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; @@ -359,9 +359,9 @@ fn parse_fn_call( capture, namespace, hash: if is_valid_identifier(id.chars()) { - FnHash::from_script(hash) + FnCallHash::from_script(hash) } else { - FnHash::from_native(hash) + FnCallHash::from_native(hash) }, args, ..Default::default() @@ -402,9 +402,9 @@ fn parse_fn_call( capture, namespace, hash: if is_valid_identifier(id.chars()) { - FnHash::from_script(hash) + FnCallHash::from_script(hash) } else { - FnHash::from_native(hash) + FnCallHash::from_native(hash) }, args, ..Default::default() @@ -1291,7 +1291,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)), + hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)), args, ..Default::default() }), @@ -1318,7 +1318,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)), + hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)), args, ..Default::default() }), @@ -1339,7 +1339,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: op.into(), - hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)), + hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)), args, ..Default::default() }), @@ -1538,7 +1538,7 @@ fn make_dot_expr( } Expr::FnCall(mut func, func_pos) => { // Recalculate hash - func.hash = FnHash::from_script_and_native( + func.hash = FnCallHash::from_script_and_native( calc_fn_hash(empty(), &func.name, func.args.len()), calc_fn_hash(empty(), &func.name, func.args.len() + 1), ); @@ -1594,7 +1594,7 @@ fn make_dot_expr( // lhs.func(...) (lhs, Expr::FnCall(mut func, func_pos)) => { // Recalculate hash - func.hash = FnHash::from_script_and_native( + func.hash = FnCallHash::from_script_and_native( calc_fn_hash(empty(), &func.name, func.args.len()), calc_fn_hash(empty(), &func.name, func.args.len() + 1), ); @@ -1682,7 +1682,7 @@ fn parse_binary_op( let op_base = FnCallExpr { name: op, - hash: FnHash::from_native(hash), + hash: FnCallHash::from_native(hash), capture: false, ..Default::default() }; @@ -1747,7 +1747,7 @@ fn parse_binary_op( let hash = calc_fn_hash(empty(), OP_CONTAINS, 2); Expr::FnCall( Box::new(FnCallExpr { - hash: FnHash::from_script(hash), + hash: FnCallHash::from_script(hash), args, name: OP_CONTAINS.into(), ..op_base @@ -1768,9 +1768,9 @@ fn parse_binary_op( Expr::FnCall( Box::new(FnCallExpr { hash: if is_valid_identifier(s.chars()) { - FnHash::from_script(hash) + FnCallHash::from_script(hash) } else { - FnHash::from_native(hash) + FnCallHash::from_native(hash) }, args, ..op_base @@ -2783,7 +2783,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po let expr = Expr::FnCall( Box::new(FnCallExpr { name: curry_func.into(), - hash: FnHash::from_native(calc_fn_hash(empty(), curry_func, num_externals + 1)), + hash: FnCallHash::from_native(calc_fn_hash(empty(), curry_func, num_externals + 1)), args, ..Default::default() }), @@ -2884,7 +2884,7 @@ fn parse_anon_fn( // Create unique function name by hashing the script body plus the parameters. let hasher = &mut get_hasher(); - params.iter().for_each(|p| p.as_str().hash(hasher)); + params.iter().for_each(|p| p.hash(hasher)); body.hash(hasher); let hash = hasher.finish(); diff --git a/src/scope.rs b/src/scope.rs index 2fa8f235..4ae73cbd 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -434,10 +434,10 @@ impl<'a> Scope<'a> { .iter() .enumerate() .rev() - .for_each(|(index, (name, alias))| { + .for_each(|(i, (name, alias))| { if !entries.names.iter().any(|(key, _)| key == name) { entries.names.push((name.clone(), alias.clone())); - entries.values.push(self.values[index].clone()); + entries.values.push(self.values[i].clone()); } }); diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 05a709a9..3f8c9bed 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -228,8 +228,8 @@ impl Engine { if include_global { self.global_modules .iter() - .flat_map(|m| m.iter_fn().map(|f| f.into())) - .for_each(|info| global.functions.push(info)); + .flat_map(|m| m.iter_fn()) + .for_each(|f| global.functions.push(f.into())); } self.global_sub_modules.iter().for_each(|(name, m)| { @@ -238,13 +238,11 @@ impl Engine { self.global_namespace .iter_fn() - .map(|f| f.into()) - .for_each(|info| global.functions.push(info)); + .for_each(|f| global.functions.push(f.into())); #[cfg(not(feature = "no_function"))] _ast.iter_functions() - .map(|f| f.into()) - .for_each(|info| global.functions.push(info)); + .for_each(|f| global.functions.push(f.into())); global.functions.sort(); diff --git a/src/utils.rs b/src/utils.rs index dd768752..3bdc9250 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -72,17 +72,16 @@ pub fn get_hasher() -> ahash::AHasher { /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline(always)] -pub fn calc_fn_hash<'a>( - mut modules: impl Iterator, - fn_name: &str, - num: usize, -) -> u64 { +pub fn calc_fn_hash<'a>(modules: impl Iterator, fn_name: &str, num: usize) -> u64 { 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 - modules.for_each(|m| m.hash(s)); + let mut len = 0; + modules + .inspect(|_| len += 1) + .skip(1) + .for_each(|m| m.hash(s)); + len.hash(s); fn_name.hash(s); num.hash(s); s.finish() @@ -96,10 +95,7 @@ pub fn calc_fn_hash<'a>( pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { let s = &mut get_hasher(); let mut len = 0; - params.for_each(|t| { - t.hash(s); - len += 1; - }); + params.inspect(|_| len += 1).for_each(|t| t.hash(s)); len.hash(s); s.finish() } From e2a66e0241e84918587bb9cece381d3dbafbac5b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Mar 2021 14:45:40 +0800 Subject: [PATCH 13/15] Fix doc tests. --- src/ast.rs | 2 +- src/module/mod.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index a3d143ae..39a5796a 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1303,7 +1303,7 @@ pub struct OpAssignment { /// /// Two separate hashes are pre-calculated because of the following pattern: /// -/// ```rust,no_run +/// ```,ignore /// func(a, b, c); // Native: func(a, b, c) - 3 parameters /// // Script: func(a, b, c) - 3 parameters /// diff --git a/src/module/mod.rs b/src/module/mod.rs index 2ca01980..123c2261 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -782,7 +782,7 @@ impl Module { /// Ok(orig) // return Result> /// }); /// - /// assert!(module.contains_fn(hash, true)); + /// assert!(module.contains_fn(hash)); /// ``` #[inline(always)] pub fn set_raw_fn( @@ -829,7 +829,7 @@ impl Module { /// /// let mut module = Module::new(); /// let hash = module.set_native_fn("calc", || Ok(42_i64)); - /// assert!(module.contains_fn(hash, true)); + /// assert!(module.contains_fn(hash)); /// ``` #[inline(always)] pub fn set_native_fn(&mut self, name: impl Into, func: F) -> u64 @@ -863,7 +863,7 @@ impl Module { /// /// let mut module = Module::new(); /// let hash = module.set_getter_fn("value", |x: &mut i64| { Ok(*x) }); - /// assert!(module.contains_fn(hash, true)); + /// assert!(module.contains_fn(hash)); /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] @@ -904,7 +904,7 @@ impl Module { /// *x = y.len() as i64; /// Ok(()) /// }); - /// assert!(module.contains_fn(hash, true)); + /// assert!(module.contains_fn(hash)); /// ``` #[cfg(not(feature = "no_object"))] #[inline(always)] @@ -949,7 +949,7 @@ impl Module { /// let hash = module.set_indexer_get_fn(|x: &mut i64, y: ImmutableString| { /// Ok(*x + y.len() as i64) /// }); - /// assert!(module.contains_fn(hash, true)); + /// assert!(module.contains_fn(hash)); /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] @@ -1010,7 +1010,7 @@ impl Module { /// *x = y.len() as i64 + value; /// Ok(()) /// }); - /// assert!(module.contains_fn(hash, true)); + /// assert!(module.contains_fn(hash)); /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] @@ -1076,8 +1076,8 @@ impl Module { /// Ok(()) /// } /// ); - /// assert!(module.contains_fn(hash_get, true)); - /// assert!(module.contains_fn(hash_set, true)); + /// assert!(module.contains_fn(hash_get)); + /// assert!(module.contains_fn(hash_set)); /// ``` #[cfg(not(feature = "no_index"))] #[inline(always)] From 80c7e9310e0d9f64158510bd1987633edb886075 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Mar 2021 15:26:37 +0800 Subject: [PATCH 14/15] Fix unchecked build. --- tests/functions.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functions.rs b/tests/functions.rs index d5b8f190..d2db1128 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -52,6 +52,7 @@ fn test_functions() -> Result<(), Box> { } #[cfg(not(feature = "no_module"))] +#[cfg(not(feature = "unchecked"))] #[test] fn test_functions_context() -> Result<(), Box> { let mut engine = Engine::new(); From 1200ffcd2b74e11426d82185fbd1d1a62424bb17 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Mar 2021 22:32:22 +0800 Subject: [PATCH 15/15] Add test with &str parameter. --- src/fn_func.rs | 20 ++++++++++---------- tests/call_fn.rs | 8 ++++++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/fn_func.rs b/src/fn_func.rs index aea3923e..a9576989 100644 --- a/src/fn_func.rs +++ b/src/fn_func.rs @@ -21,9 +21,9 @@ pub trait Func { /// /// ``` /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, Func}; // use 'Func' for 'create_from_ast' + /// use rhai::{Engine, Func}; // use 'Func' for 'create_from_ast' /// - /// let engine = Engine::new(); // create a new 'Engine' just for this + /// let engine = Engine::new(); // create a new 'Engine' just for this /// /// let ast = engine.compile("fn calc(x, y) { x + len(y) < 42 }")?; /// @@ -32,15 +32,15 @@ pub trait Func { /// // 2) the return type of the script function /// /// // 'func' will have type Box Result>> and is callable! - /// let func = Func::<(i64, String), bool>::create_from_ast( - /// // ^^^^^^^^^^^^^ function parameter types in tuple + /// let func = Func::<(i64, &str), bool>::create_from_ast( + /// // ^^^^^^^^^^^ function parameter types in tuple /// /// engine, // the 'Engine' is consumed into the closure /// ast, // the 'AST' /// "calc" // the entry-point function name /// ); /// - /// func(123, "hello".to_string())? == false; // call the anonymous function + /// func(123, "hello")? == false; // call the anonymous function /// # Ok(()) /// # } fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output; @@ -53,9 +53,9 @@ pub trait Func { /// /// ``` /// # fn main() -> Result<(), Box> { - /// use rhai::{Engine, Func}; // use 'Func' for 'create_from_script' + /// use rhai::{Engine, Func}; // use 'Func' for 'create_from_script' /// - /// let engine = Engine::new(); // create a new 'Engine' just for this + /// let engine = Engine::new(); // create a new 'Engine' just for this /// /// let script = "fn calc(x, y) { x + len(y) < 42 }"; /// @@ -64,15 +64,15 @@ pub trait Func { /// // 2) the return type of the script function /// /// // 'func' will have type Box Result>> and is callable! - /// let func = Func::<(i64, String), bool>::create_from_script( - /// // ^^^^^^^^^^^^^ function parameter types in tuple + /// let func = Func::<(i64, &str), bool>::create_from_script( + /// // ^^^^^^^^^^^ function parameter types in tuple /// /// engine, // the 'Engine' is consumed into the closure /// script, // the script, notice number of parameters must match /// "calc" // the entry-point function name /// )?; /// - /// func(123, "hello".to_string())? == false; // call the anonymous function + /// func(123, "hello")? == false; // call the anonymous function /// # Ok(()) /// # } /// ``` diff --git a/tests/call_fn.rs b/tests/call_fn.rs index dbe3ece0..fdf82e88 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -215,5 +215,13 @@ fn test_anonymous_fn() -> Result<(), Box> { assert_eq!(calc_func(42, "hello".to_string(), 9)?, 423); + let calc_func = Func::<(INT, &str, INT), INT>::create_from_script( + Engine::new(), + "fn calc(x, y, z) { (x + len(y)) * z }", + "calc", + )?; + + assert_eq!(calc_func(42, "hello", 9)?, 423); + Ok(()) }