diff --git a/src/engine.rs b/src/engine.rs index 8428de09..926e2e8f 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -3,11 +3,12 @@ use crate::any::{Dynamic, Union}; use crate::calc_fn_hash; use crate::error::ParseErrorType; +use crate::fn_native::{FnCallArgs, NativeFunction, SharedNativeFunction}; use crate::optimize::OptimizationLevel; use crate::packages::{ CorePackage, Package, PackageLibrary, PackageStore, PackagesCollection, StandardPackage, }; -use crate::parser::{Expr, FnAccess, FnDef, ReturnType, Stmt, AST}; +use crate::parser::{Expr, FnAccess, FnDef, ReturnType, SharedFnDef, Stmt, AST}; use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::token::Position; @@ -46,19 +47,6 @@ pub type Array = Vec; #[cfg(not(feature = "no_object"))] pub type Map = HashMap; -pub type FnCallArgs<'a> = [&'a mut Dynamic]; - -#[cfg(feature = "sync")] -pub type FnAny = - dyn Fn(&mut FnCallArgs, Position) -> Result> + Send + Sync; -#[cfg(not(feature = "sync"))] -pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result>; - -#[cfg(feature = "sync")] -pub type IteratorFn = dyn Fn(Dynamic) -> Box> + Send + Sync; -#[cfg(not(feature = "sync"))] -pub type IteratorFn = dyn Fn(Dynamic) -> Box>; - #[cfg(debug_assertions)] pub const MAX_CALL_STACK_DEPTH: usize = 28; @@ -168,20 +156,6 @@ impl<'a> State<'a> { } } -/// An external native Rust function. -#[cfg(not(feature = "sync"))] -pub type NativeFunction = Rc>; -/// An external native Rust function. -#[cfg(feature = "sync")] -pub type NativeFunction = Arc>; - -/// A sharable script-defined function. -#[cfg(feature = "sync")] -pub type ScriptedFunction = Arc; -/// A sharable script-defined function. -#[cfg(not(feature = "sync"))] -pub type ScriptedFunction = Rc; - /// A type that holds a library (`HashMap`) of script-defined functions. /// /// Since script-defined functions have `Dynamic` parameters, functions with the same name @@ -190,7 +164,7 @@ pub type ScriptedFunction = Rc; /// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_hash` /// with dummy parameter types `EMPTY_TYPE_ID()` repeated the correct number of times. #[derive(Debug, Clone, Default)] -pub struct FunctionsLib(HashMap); +pub struct FunctionsLib(HashMap); impl FunctionsLib { /// Create a new `FunctionsLib` from a collection of `FnDef`. @@ -261,8 +235,8 @@ impl FunctionsLib { } } -impl From> for FunctionsLib { - fn from(values: Vec<(u64, ScriptedFunction)>) -> Self { +impl From> for FunctionsLib { + fn from(values: Vec<(u64, SharedFnDef)>) -> Self { FunctionsLib(values.into_iter().collect()) } } @@ -596,7 +570,7 @@ impl Engine { .or_else(|| self.packages.get_function(hash_fn_spec)) { // Run external function - let result = func(args, pos)?; + let result = func.call(args, pos)?; // See if the function match print/debug (which requires special processing) return Ok(match fn_name { @@ -1476,7 +1450,7 @@ impl Engine { let hash = *hash_fn_def ^ hash_fn_args; match module.get_qualified_fn(name, hash, *pos) { - Ok(func) => func(args.as_mut(), *pos), + Ok(func) => func.call(args.as_mut(), *pos), Err(_) if def_val.is_some() => Ok(def_val.clone().unwrap()), Err(err) => Err(err), } diff --git a/src/fn_native.rs b/src/fn_native.rs new file mode 100644 index 00000000..dfabc1d3 --- /dev/null +++ b/src/fn_native.rs @@ -0,0 +1,52 @@ +use crate::any::Dynamic; +use crate::result::EvalAltResult; +use crate::token::Position; + +use crate::stdlib::{boxed::Box, rc::Rc, sync::Arc}; + +pub type FnCallArgs<'a> = [&'a mut Dynamic]; + +#[cfg(feature = "sync")] +pub type FnAny = + dyn Fn(&mut FnCallArgs, Position) -> Result> + Send + Sync; +#[cfg(not(feature = "sync"))] +pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result>; + +#[cfg(feature = "sync")] +pub type IteratorFn = dyn Fn(Dynamic) -> Box> + Send + Sync; +#[cfg(not(feature = "sync"))] +pub type IteratorFn = dyn Fn(Dynamic) -> Box>; + +/// A trait implemented by all native Rust functions that are callable by Rhai. +pub trait NativeCallable { + /// Call a native Rust function. + fn call(&self, args: &mut FnCallArgs, pos: Position) -> Result>; +} + +/// A type encapsulating a native Rust function callable by Rhai. +pub struct NativeFunction(Box); + +impl NativeCallable for NativeFunction { + fn call(&self, args: &mut FnCallArgs, pos: Position) -> Result> { + (self.0)(args, pos) + } +} + +impl From> for NativeFunction { + fn from(func: Box) -> Self { + Self::new(func) + } +} +impl NativeFunction { + /// Create a new `NativeFunction`. + pub fn new(func: Box) -> Self { + Self(func) + } +} + +/// An external native Rust function. +#[cfg(not(feature = "sync"))] +pub type SharedNativeFunction = Rc>; +/// An external native Rust function. +#[cfg(feature = "sync")] +pub type SharedNativeFunction = Arc>; diff --git a/src/fn_register.rs b/src/fn_register.rs index f58b023d..a4e7d214 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -3,7 +3,8 @@ #![allow(non_snake_case)] use crate::any::{Dynamic, Variant}; -use crate::engine::{Engine, FnCallArgs}; +use crate::engine::Engine; +use crate::fn_native::{FnCallArgs, NativeFunction}; use crate::result::EvalAltResult; use crate::token::Position; use crate::utils::calc_fn_spec; @@ -117,14 +118,14 @@ pub struct Mut(T); //pub struct Ref(T); /// Dereference into &mut. -#[inline] +#[inline(always)] pub fn by_ref(data: &mut Dynamic) -> &mut T { // Directly cast the &mut Dynamic into &mut T to access the underlying data. data.downcast_mut::().unwrap() } /// Dereference into value. -#[inline] +#[inline(always)] pub fn by_value(data: &mut Dynamic) -> T { // 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. @@ -133,14 +134,13 @@ pub fn by_value(data: &mut Dynamic) -> T { /// This macro creates a closure wrapping a registered function. macro_rules! make_func { - ($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => { -// ^ function name -// ^ function pointer -// ^ result mapping function -// ^ function parameter generic type name (A, B, C etc.) -// ^ dereferencing function + ($fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => { +// ^ function pointer +// ^ result mapping function +// ^ function parameter generic type name (A, B, C etc.) +// ^ dereferencing function - move |args: &mut FnCallArgs, pos: Position| { + NativeFunction::new(Box::new(move |args: &mut FnCallArgs, pos: Position| { // The arguments are assumed to be of the correct number and types! #[allow(unused_variables, unused_mut)] @@ -156,12 +156,12 @@ macro_rules! make_func { // Map the result $map(r, pos) - }; + })); }; } /// To Dynamic mapping function. -#[inline] +#[inline(always)] pub fn map_dynamic( data: T, _pos: Position, @@ -170,13 +170,13 @@ pub fn map_dynamic( } /// To Dynamic mapping function. -#[inline] +#[inline(always)] pub fn map_identity(data: Dynamic, _pos: Position) -> Result> { Ok(data) } /// To `Result>` mapping function. -#[inline] +#[inline(always)] pub fn map_result( data: Result>, pos: Position, @@ -207,8 +207,7 @@ macro_rules! def_register { > RegisterFn for Engine { fn register_fn(&mut self, name: &str, f: FN) { - let fn_name = name.to_string(); - let func = make_func!(fn_name : f : map_dynamic ; $($par => $clone),*); + let func = make_func!(f : map_dynamic ; $($par => $clone),*); let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned()); self.base_package.functions.insert(hash, Box::new(func)); } @@ -225,8 +224,7 @@ macro_rules! def_register { > RegisterDynamicFn for Engine { fn register_dynamic_fn(&mut self, name: &str, f: FN) { - let fn_name = name.to_string(); - let func = make_func!(fn_name : f : map_identity ; $($par => $clone),*); + let func = make_func!(f : map_identity ; $($par => $clone),*); let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned()); self.base_package.functions.insert(hash, Box::new(func)); } @@ -244,8 +242,7 @@ macro_rules! def_register { > RegisterResultFn for Engine { fn register_result_fn(&mut self, name: &str, f: FN) { - let fn_name = name.to_string(); - let func = make_func!(fn_name : f : map_result ; $($par => $clone),*); + let func = make_func!(f : map_result ; $($par => $clone),*); let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned()); self.base_package.functions.insert(hash, Box::new(func)); } diff --git a/src/lib.rs b/src/lib.rs index 1bdf8fd7..a912bdab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,7 @@ mod engine; mod error; mod fn_call; mod fn_func; +mod fn_native; mod fn_register; mod module; mod optimize; @@ -90,6 +91,7 @@ pub use any::Dynamic; pub use engine::Engine; pub use error::{ParseError, ParseErrorType}; pub use fn_call::FuncArgs; +pub use fn_native::NativeCallable; pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn}; pub use parser::{AST, INT}; pub use result::EvalAltResult; diff --git a/src/module.rs b/src/module.rs index 0cf82564..284d662b 100644 --- a/src/module.rs +++ b/src/module.rs @@ -3,8 +3,9 @@ use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; -use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib, NativeFunction, ScriptedFunction}; -use crate::parser::{FnAccess, FnDef, AST}; +use crate::engine::{Engine, FunctionsLib}; +use crate::fn_native::{FnAny, FnCallArgs, NativeCallable, NativeFunction, SharedNativeFunction}; +use crate::parser::{FnAccess, FnDef, SharedFnDef, AST}; use crate::result::EvalAltResult; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::token::{Position, Token}; @@ -57,10 +58,10 @@ pub struct Module { all_variables: HashMap, /// External Rust functions. - functions: HashMap, NativeFunction)>, + functions: HashMap, SharedNativeFunction)>, /// Flattened collection of all external Rust functions, including those in sub-modules. - all_functions: HashMap, + all_functions: HashMap, /// Script-defined functions. fn_lib: FunctionsLib, @@ -269,12 +270,14 @@ impl Module { ) -> u64 { let hash = calc_fn_hash(empty(), &fn_name, params.iter().cloned()); + let f = Box::new(NativeFunction::from(func)) as Box; + #[cfg(not(feature = "sync"))] - self.functions - .insert(hash, (fn_name, access, params, Rc::new(func))); + let func = Rc::new(f); #[cfg(feature = "sync")] - self.functions - .insert(hash, (fn_name, access, params, Arc::new(func))); + let func = Arc::new(f); + + self.functions.insert(hash, (fn_name, access, params, func)); hash } @@ -528,7 +531,7 @@ impl Module { /// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1)); /// assert!(module.get_fn(hash).is_some()); /// ``` - pub fn get_fn(&self, hash: u64) -> Option<&Box> { + pub fn get_fn(&self, hash: u64) -> Option<&Box> { self.functions.get(&hash).map(|(_, _, _, v)| v.as_ref()) } @@ -541,7 +544,7 @@ impl Module { name: &str, hash: u64, pos: Position, - ) -> Result<&Box, Box> { + ) -> Result<&Box, Box> { self.all_functions .get(&hash) .map(|f| f.as_ref()) @@ -626,8 +629,8 @@ impl Module { module: &'a mut Module, qualifiers: &mut Vec<&'a str>, variables: &mut Vec<(u64, Dynamic)>, - functions: &mut Vec<(u64, NativeFunction)>, - fn_lib: &mut Vec<(u64, ScriptedFunction)>, + functions: &mut Vec<(u64, SharedNativeFunction)>, + fn_lib: &mut Vec<(u64, SharedFnDef)>, ) { for (name, m) in module.modules.iter_mut() { // Index all the sub-modules first. diff --git a/src/optimize.rs b/src/optimize.rs index ca18d1ce..060b9c3c 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,9 +1,9 @@ use crate::any::Dynamic; use crate::calc_fn_hash; use crate::engine::{ - Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, - KEYWORD_TYPE_OF, + Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; +use crate::fn_native::FnCallArgs; use crate::packages::{PackageStore, PackagesCollection}; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST}; use crate::result::EvalAltResult; @@ -123,7 +123,7 @@ fn call_fn( base_package .get_function(hash) .or_else(|| packages.get_function(hash)) - .map(|func| func(args, pos)) + .map(|func| func.call(args, pos)) .transpose() } diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 0d935e40..f9b50d90 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -1,6 +1,6 @@ //! This module contains all built-in _packages_ available to Rhai, plus facilities to define custom packages. -use crate::engine::{FnAny, IteratorFn}; +use crate::fn_native::{IteratorFn, NativeCallable}; use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec}; @@ -48,7 +48,7 @@ pub trait Package { /// Type to store all functions in the package. pub struct PackageStore { /// All functions, keyed by a hash created from the function name and parameter types. - pub functions: HashMap>, + pub functions: HashMap>, /// All iterator functions, keyed by the type producing the iterator. pub type_iterators: HashMap>, @@ -64,7 +64,7 @@ impl PackageStore { self.functions.contains_key(&hash) } /// Get specified function via its hash key. - pub fn get_function(&self, hash: u64) -> Option<&Box> { + pub fn get_function(&self, hash: u64) -> Option<&Box> { self.functions.get(&hash) } /// Does the specified TypeId iterator exist in the `PackageStore`? @@ -113,7 +113,7 @@ impl PackagesCollection { self.packages.iter().any(|p| p.contains_function(hash)) } /// Get specified function via its hash key. - pub fn get_function(&self, hash: u64) -> Option<&Box> { + pub fn get_function(&self, hash: u64) -> Option<&Box> { self.packages .iter() .map(|p| p.get_function(hash)) diff --git a/src/packages/utils.rs b/src/packages/utils.rs index 7a6e2231..d4783811 100644 --- a/src/packages/utils.rs +++ b/src/packages/utils.rs @@ -2,7 +2,7 @@ use super::PackageStore; use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; -use crate::engine::FnCallArgs; +use crate::fn_native::{FnCallArgs, NativeFunction}; use crate::result::EvalAltResult; use crate::token::Position; @@ -106,7 +106,7 @@ pub fn reg_none( map_result(r, pos) }); - lib.functions.insert(hash, f); + lib.functions.insert(hash, Box::new(NativeFunction::new(f))); } /// Add a function with one parameter to the package. @@ -157,7 +157,7 @@ pub fn reg_unary( map_result(r, pos) }); - lib.functions.insert(hash, f); + lib.functions.insert(hash, Box::new(NativeFunction::new(f))); } /// Add a function with one mutable reference parameter to the package. @@ -215,7 +215,7 @@ pub fn reg_unary_mut( map_result(r, pos) }); - lib.functions.insert(hash, f); + lib.functions.insert(hash, Box::new(NativeFunction::new(f))); } /// Add a function with two parameters to the package. @@ -271,7 +271,7 @@ pub fn reg_binary( map_result(r, pos) }); - lib.functions.insert(hash, f); + lib.functions.insert(hash, Box::new(NativeFunction::new(f))); } /// Add a function with two parameters (the first one being a mutable reference) to the package. @@ -334,7 +334,7 @@ pub fn reg_binary_mut( map_result(r, pos) }); - lib.functions.insert(hash, f); + lib.functions.insert(hash, Box::new(NativeFunction::new(f))); } /// Add a function with three parameters to the package. @@ -374,7 +374,7 @@ pub fn reg_trinary; +/// A sharable script-defined function. +#[cfg(not(feature = "sync"))] +pub type SharedFnDef = Rc; + /// `return`/`throw` statement. #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum ReturnType {