diff --git a/src/api.rs b/src/api.rs index 01d0a76f..40eef3c3 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,7 +1,7 @@ //! Module that defines the extern API of `Engine`. use crate::any::{Dynamic, Variant}; -use crate::engine::{make_getter, make_setter, Engine, FnAny, FnSpec, Map}; +use crate::engine::{calc_fn_spec, make_getter, make_setter, Engine, FnAny, Map}; use crate::error::ParseError; use crate::fn_call::FuncArgs; use crate::fn_register::RegisterFn; @@ -59,18 +59,16 @@ pub trait IteratorCallback: Fn(&Dynamic) -> Box> + impl Box> + 'static> IteratorCallback for F {} /// Engine public API -impl<'e> Engine<'e> { +impl Engine { /// Register a custom function. pub(crate) fn register_fn_raw(&mut self, fn_name: &str, args: Vec, f: Box) { - let spec = FnSpec { - name: fn_name.to_string().into(), - args, - }; - if self.functions.is_none() { self.functions = Some(HashMap::new()); } - self.functions.as_mut().unwrap().insert(spec, f); + self.functions + .as_mut() + .unwrap() + .insert(calc_fn_spec(fn_name, args.into_iter()), f); } /// Register a custom type for use with the `Engine`. @@ -969,23 +967,25 @@ impl<'e> Engine<'e> { /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { /// # use std::sync::RwLock; + /// # use std::sync::Arc; /// use rhai::Engine; /// - /// let result = RwLock::new(String::from("")); - /// { - /// let mut engine = Engine::new(); + /// let result = Arc::new(RwLock::new(String::from(""))); /// - /// // Override action of 'print' function - /// engine.on_print(|s| result.write().unwrap().push_str(s)); + /// let mut engine = Engine::new(); + /// + /// // Override action of 'print' function + /// let logger = result.clone(); + /// engine.on_print(move |s| logger.write().unwrap().push_str(s)); + /// + /// engine.consume("print(40 + 2);")?; /// - /// engine.consume("print(40 + 2);")?; - /// } /// assert_eq!(*result.read().unwrap(), "42"); /// # Ok(()) /// # } /// ``` #[cfg(feature = "sync")] - pub fn on_print(&mut self, callback: impl Fn(&str) + Send + Sync + 'e) { + pub fn on_print(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) { self.on_print = Some(Box::new(callback)); } /// Override default action of `print` (print to stdout using `println!`) @@ -995,23 +995,25 @@ impl<'e> Engine<'e> { /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { /// # use std::sync::RwLock; + /// # use std::sync::Arc; /// use rhai::Engine; /// - /// let result = RwLock::new(String::from("")); - /// { - /// let mut engine = Engine::new(); + /// let result = Arc::new(RwLock::new(String::from(""))); /// - /// // Override action of 'print' function - /// engine.on_print(|s| result.write().unwrap().push_str(s)); + /// let mut engine = Engine::new(); + /// + /// // Override action of 'print' function + /// let logger = result.clone(); + /// engine.on_print(move |s| logger.write().unwrap().push_str(s)); + /// + /// engine.consume("print(40 + 2);")?; /// - /// engine.consume("print(40 + 2);")?; - /// } /// assert_eq!(*result.read().unwrap(), "42"); /// # Ok(()) /// # } /// ``` #[cfg(not(feature = "sync"))] - pub fn on_print(&mut self, callback: impl Fn(&str) + 'e) { + pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) { self.on_print = Some(Box::new(callback)); } @@ -1022,23 +1024,25 @@ impl<'e> Engine<'e> { /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { /// # use std::sync::RwLock; + /// # use std::sync::Arc; /// use rhai::Engine; /// - /// let result = RwLock::new(String::from("")); - /// { - /// let mut engine = Engine::new(); + /// let result = Arc::new(RwLock::new(String::from(""))); /// - /// // Override action of 'print' function - /// engine.on_debug(|s| result.write().unwrap().push_str(s)); + /// let mut engine = Engine::new(); + /// + /// // Override action of 'print' function + /// let logger = result.clone(); + /// engine.on_debug(move |s| logger.write().unwrap().push_str(s)); + /// + /// engine.consume(r#"debug("hello");"#)?; /// - /// engine.consume(r#"debug("hello");"#)?; - /// } /// assert_eq!(*result.read().unwrap(), r#""hello""#); /// # Ok(()) /// # } /// ``` #[cfg(feature = "sync")] - pub fn on_debug(&mut self, callback: impl Fn(&str) + Send + Sync + 'e) { + pub fn on_debug(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) { self.on_debug = Some(Box::new(callback)); } /// Override default action of `debug` (print to stdout using `println!`) @@ -1048,23 +1052,25 @@ impl<'e> Engine<'e> { /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { /// # use std::sync::RwLock; + /// # use std::sync::Arc; /// use rhai::Engine; /// - /// let result = RwLock::new(String::from("")); - /// { - /// let mut engine = Engine::new(); + /// let result = Arc::new(RwLock::new(String::from(""))); /// - /// // Override action of 'print' function - /// engine.on_debug(|s| result.write().unwrap().push_str(s)); + /// let mut engine = Engine::new(); + /// + /// // Override action of 'print' function + /// let logger = result.clone(); + /// engine.on_debug(move |s| logger.write().unwrap().push_str(s)); + /// + /// engine.consume(r#"debug("hello");"#)?; /// - /// engine.consume(r#"debug("hello");"#)?; - /// } /// assert_eq!(*result.read().unwrap(), r#""hello""#); /// # Ok(()) /// # } /// ``` #[cfg(not(feature = "sync"))] - pub fn on_debug(&mut self, callback: impl Fn(&str) + 'e) { + pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) { self.on_debug = Some(Box::new(callback)); } } diff --git a/src/builtin.rs b/src/builtin.rs index 0fac046f..428de9b7 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -94,7 +94,7 @@ fn ne(x: T, y: T) -> bool { x != y } -impl Engine<'_> { +impl Engine { /// Register the core built-in library. pub(crate) fn register_core_lib(&mut self) { // Checked add @@ -770,7 +770,7 @@ macro_rules! reg_fn2y { } /// Register the built-in library. -impl Engine<'_> { +impl Engine { pub fn register_stdlib(&mut self) { #[cfg(not(feature = "no_float"))] { diff --git a/src/engine.rs b/src/engine.rs index 1052d080..2e1462d9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -10,17 +10,16 @@ use crate::token::Position; use crate::stdlib::{ any::TypeId, - borrow::Cow, boxed::Box, cmp::Ordering, - collections::HashMap, + collections::{hash_map::DefaultHasher, HashMap}, format, + hash::{Hash, Hasher}, iter::once, ops::{Deref, DerefMut}, rc::Rc, string::{String, ToString}, sync::Arc, - vec, vec::Vec, }; @@ -114,12 +113,6 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> { } } -#[derive(Debug, Eq, PartialEq, Hash, Clone)] -pub struct FnSpec<'a> { - pub name: Cow<'a, str>, - pub args: Vec, -} - /// A type that holds a library of script-defined functions. /// /// Since script-defined functions have `Dynamic` parameters, functions with the same name @@ -239,9 +232,9 @@ impl DerefMut for FunctionsLib { /// ``` /// /// Currently, `Engine` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. -pub struct Engine<'e> { +pub struct Engine { /// A hashmap containing all compiled functions known to the engine. - pub(crate) functions: Option, Box>>, + pub(crate) functions: Option>>, /// A hashmap containing all iterators known to the engine. pub(crate) type_iterators: Option>>, @@ -250,17 +243,17 @@ pub struct Engine<'e> { /// Closure for implementing the `print` command. #[cfg(feature = "sync")] - pub(crate) on_print: Option>, + pub(crate) on_print: Option>, /// Closure for implementing the `print` command. #[cfg(not(feature = "sync"))] - pub(crate) on_print: Option>, + pub(crate) on_print: Option>, /// Closure for implementing the `debug` command. #[cfg(feature = "sync")] - pub(crate) on_debug: Option>, + pub(crate) on_debug: Option>, /// Closure for implementing the `debug` command. #[cfg(not(feature = "sync"))] - pub(crate) on_debug: Option>, + pub(crate) on_debug: Option>, /// Optimize the AST after compilation. pub(crate) optimization_level: OptimizationLevel, @@ -271,7 +264,7 @@ pub struct Engine<'e> { pub(crate) max_call_stack_depth: usize, } -impl Default for Engine<'_> { +impl Default for Engine { fn default() -> Self { // Create the new scripting Engine let mut engine = Engine { @@ -349,7 +342,14 @@ fn extract_prop_from_setter(fn_name: &str) -> Option<&str> { } } -impl Engine<'_> { +pub(crate) fn calc_fn_spec(fn_name: &str, params: impl Iterator) -> u64 { + let mut s = DefaultHasher::new(); + fn_name.hash(&mut s); + params.for_each(|t| t.hash(&mut s)); + s.finish() +} + +impl Engine { /// Create a new `Engine` pub fn new() -> Self { Default::default() @@ -471,11 +471,6 @@ impl Engine<'_> { } } - let spec = FnSpec { - name: fn_name.into(), - args: args.iter().map(|a| a.type_id()).collect(), - }; - // Argument must be a string fn cast_to_string(r: &Dynamic, pos: Position) -> Result<&str, EvalAltResult> { r.as_str() @@ -483,7 +478,9 @@ impl Engine<'_> { } // Search built-in's and external functions - if let Some(func) = self.functions.as_ref().and_then(|f| f.get(&spec)) { + let fn_spec = calc_fn_spec(fn_name, args.iter().map(|a| a.type_id())); + + if let Some(func) = self.functions.as_ref().and_then(|f| f.get(&fn_spec)) { // Run external function let result = func(args, pos)?; @@ -519,7 +516,7 @@ impl Engine<'_> { } if let Some(prop) = extract_prop_from_setter(fn_name) { - let (arg, value) = args.split_at_mut(0); + let (arg, value) = args.split_at_mut(1); return match arg[0] { // Map property update @@ -1336,10 +1333,8 @@ impl Engine<'_> { name: &str, ) -> bool { engine.functions.as_ref().map_or(false, |lib| { - lib.contains_key(&FnSpec { - name: name.into(), - args: vec![TypeId::of::()], - }) + let fn_spec = calc_fn_spec(name, [TypeId::of::()].iter().cloned()); + lib.contains_key(&fn_spec) }) || fn_lib.map_or(false, |lib| lib.has_function(name, 1)) } diff --git a/src/fn_func.rs b/src/fn_func.rs index af45dae8..4c3544de 100644 --- a/src/fn_func.rs +++ b/src/fn_func.rs @@ -88,13 +88,13 @@ macro_rules! def_anonymous_fn { def_anonymous_fn!(imp); }; (imp $($par:ident),*) => { - impl<'e, $($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine<'e> + impl<$($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine { #[cfg(feature = "sync")] - type Output = Box Result + Send + Sync + 'e>; + type Output = Box Result + Send + Sync>; #[cfg(not(feature = "sync"))] - type Output = Box Result + 'e>; + type Output = Box Result>; fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output { let name = entry_point.to_string(); diff --git a/src/fn_register.rs b/src/fn_register.rs index 711c9e4b..08bf9109 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -146,7 +146,7 @@ macro_rules! def_register { FN: Fn($($param),*) -> RET + 'static, RET: Variant + Clone - > RegisterFn for Engine<'_> + > RegisterFn for Engine { fn register_fn(&mut self, name: &str, f: FN) { let fn_name = name.to_string(); @@ -184,7 +184,7 @@ macro_rules! def_register { #[cfg(not(feature = "sync"))] FN: Fn($($param),*) -> Dynamic + 'static, - > RegisterDynamicFn for Engine<'_> + > RegisterDynamicFn for Engine { fn register_dynamic_fn(&mut self, name: &str, f: FN) { let fn_name = name.to_string(); @@ -221,7 +221,7 @@ macro_rules! def_register { FN: Fn($($param),*) -> Result + 'static, RET: Variant + Clone - > RegisterResultFn for Engine<'_> + > RegisterResultFn for Engine { fn register_result_fn(&mut self, name: &str, f: FN) { let fn_name = name.to_string(); diff --git a/src/optimize.rs b/src/optimize.rs index cd4c30b5..1e8161e7 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,7 +1,7 @@ use crate::any::Dynamic; use crate::engine::{ - Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, - KEYWORD_TYPE_OF, + calc_fn_spec, Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, + KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST}; use crate::result::EvalAltResult; @@ -50,7 +50,7 @@ struct State<'a> { /// Collection of constants to use for eager function evaluations. constants: Vec<(String, Expr)>, /// An `Engine` instance for eager function evaluation. - engine: &'a Engine<'a>, + engine: &'a Engine, /// Library of script-defined functions. fn_lib: &'a [(&'a str, usize)], /// Optimization level. @@ -60,7 +60,7 @@ struct State<'a> { impl<'a> State<'a> { /// Create a new State. pub fn new( - engine: &'a Engine<'a>, + engine: &'a Engine, fn_lib: &'a [(&'a str, usize)], level: OptimizationLevel, ) -> Self { @@ -110,19 +110,14 @@ impl<'a> State<'a> { /// Call a registered function fn call_fn( - functions: Option<&HashMap>>, + functions: Option<&HashMap>>, fn_name: &str, args: &mut FnCallArgs, pos: Position, ) -> Result, EvalAltResult> { - let spec = FnSpec { - name: fn_name.into(), - args: args.iter().map(|a| a.type_id()).collect(), - }; - // Search built-in's and external functions functions - .and_then(|f| f.get(&spec)) + .and_then(|f| f.get(&calc_fn_spec(fn_name, args.iter().map(|a| a.type_id())))) .map(|func| func(args, pos)) .transpose() } @@ -621,7 +616,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { fn optimize<'a>( statements: Vec, - engine: &Engine<'a>, + engine: &Engine, scope: &Scope, fn_lib: &'a [(&'a str, usize)], level: OptimizationLevel, diff --git a/src/parser.rs b/src/parser.rs index 755609af..c8331d35 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1759,9 +1759,9 @@ fn parse_fn<'a>( }) } -pub fn parse_global_expr<'a, 'e>( +pub fn parse_global_expr<'a>( input: &mut Peekable>, - engine: &Engine<'e>, + engine: &Engine, scope: &Scope, optimization_level: OptimizationLevel, ) -> Result { @@ -1841,9 +1841,9 @@ fn parse_global_level<'a>( } /// Run the parser on an input stream, returning an AST. -pub fn parse<'a, 'e>( +pub fn parse<'a>( input: &mut Peekable>, - engine: &Engine<'e>, + engine: &Engine, scope: &Scope, optimization_level: OptimizationLevel, ) -> Result { diff --git a/tests/side_effects.rs b/tests/side_effects.rs index 0375d1af..259d1f63 100644 --- a/tests/side_effects.rs +++ b/tests/side_effects.rs @@ -81,18 +81,18 @@ fn test_side_effects_command() -> Result<(), EvalAltResult> { #[test] fn test_side_effects_print() -> Result<(), EvalAltResult> { + use std::sync::Arc; use std::sync::RwLock; - let result = RwLock::new(String::from("")); + let result = Arc::new(RwLock::new(String::from(""))); - { - let mut engine = Engine::new(); + let mut engine = Engine::new(); - // Override action of 'print' function - engine.on_print(|s| result.write().unwrap().push_str(s)); + // Override action of 'print' function + let logger = result.clone(); + engine.on_print(move |s| logger.write().unwrap().push_str(s)); - engine.consume("print(40 + 2);")?; - } + engine.consume("print(40 + 2);")?; assert_eq!(*result.read().unwrap(), "42"); Ok(())