Remove lifetime from Engine.

This commit is contained in:
Stephen Chung 2020-04-16 23:31:48 +08:00
parent 4f2350734f
commit f8e9d66a0b
8 changed files with 95 additions and 99 deletions

View File

@ -1,7 +1,7 @@
//! Module that defines the extern API of `Engine`. //! Module that defines the extern API of `Engine`.
use crate::any::{Dynamic, Variant}; 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::error::ParseError;
use crate::fn_call::FuncArgs; use crate::fn_call::FuncArgs;
use crate::fn_register::RegisterFn; use crate::fn_register::RegisterFn;
@ -59,18 +59,16 @@ pub trait IteratorCallback: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> +
impl<F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static> IteratorCallback for F {} impl<F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static> IteratorCallback for F {}
/// Engine public API /// Engine public API
impl<'e> Engine<'e> { impl Engine {
/// Register a custom function. /// Register a custom function.
pub(crate) fn register_fn_raw(&mut self, fn_name: &str, args: Vec<TypeId>, f: Box<FnAny>) { pub(crate) fn register_fn_raw(&mut self, fn_name: &str, args: Vec<TypeId>, f: Box<FnAny>) {
let spec = FnSpec {
name: fn_name.to_string().into(),
args,
};
if self.functions.is_none() { if self.functions.is_none() {
self.functions = Some(HashMap::new()); 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`. /// Register a custom type for use with the `Engine`.
@ -969,23 +967,25 @@ impl<'e> Engine<'e> {
/// ``` /// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # use std::sync::RwLock; /// # use std::sync::RwLock;
/// # use std::sync::Arc;
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let result = RwLock::new(String::from("")); /// let result = Arc::new(RwLock::new(String::from("")));
/// {
/// let mut engine = Engine::new();
/// ///
/// // Override action of 'print' function /// let mut engine = Engine::new();
/// 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"); /// assert_eq!(*result.read().unwrap(), "42");
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(feature = "sync")] #[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)); self.on_print = Some(Box::new(callback));
} }
/// Override default action of `print` (print to stdout using `println!`) /// Override default action of `print` (print to stdout using `println!`)
@ -995,23 +995,25 @@ impl<'e> Engine<'e> {
/// ``` /// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # use std::sync::RwLock; /// # use std::sync::RwLock;
/// # use std::sync::Arc;
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let result = RwLock::new(String::from("")); /// let result = Arc::new(RwLock::new(String::from("")));
/// {
/// let mut engine = Engine::new();
/// ///
/// // Override action of 'print' function /// let mut engine = Engine::new();
/// 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"); /// assert_eq!(*result.read().unwrap(), "42");
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "sync"))] #[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)); self.on_print = Some(Box::new(callback));
} }
@ -1022,23 +1024,25 @@ impl<'e> Engine<'e> {
/// ``` /// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # use std::sync::RwLock; /// # use std::sync::RwLock;
/// # use std::sync::Arc;
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let result = RwLock::new(String::from("")); /// let result = Arc::new(RwLock::new(String::from("")));
/// {
/// let mut engine = Engine::new();
/// ///
/// // Override action of 'print' function /// let mut engine = Engine::new();
/// engine.on_debug(|s| result.write().unwrap().push_str(s)); ///
/// // 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""#); /// assert_eq!(*result.read().unwrap(), r#""hello""#);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(feature = "sync")] #[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)); self.on_debug = Some(Box::new(callback));
} }
/// Override default action of `debug` (print to stdout using `println!`) /// Override default action of `debug` (print to stdout using `println!`)
@ -1048,23 +1052,25 @@ impl<'e> Engine<'e> {
/// ``` /// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # use std::sync::RwLock; /// # use std::sync::RwLock;
/// # use std::sync::Arc;
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let result = RwLock::new(String::from("")); /// let result = Arc::new(RwLock::new(String::from("")));
/// {
/// let mut engine = Engine::new();
/// ///
/// // Override action of 'print' function /// let mut engine = Engine::new();
/// engine.on_debug(|s| result.write().unwrap().push_str(s)); ///
/// // 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""#); /// assert_eq!(*result.read().unwrap(), r#""hello""#);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "sync"))] #[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)); self.on_debug = Some(Box::new(callback));
} }
} }

View File

@ -94,7 +94,7 @@ fn ne<T: PartialEq>(x: T, y: T) -> bool {
x != y x != y
} }
impl Engine<'_> { impl Engine {
/// Register the core built-in library. /// Register the core built-in library.
pub(crate) fn register_core_lib(&mut self) { pub(crate) fn register_core_lib(&mut self) {
// Checked add // Checked add
@ -770,7 +770,7 @@ macro_rules! reg_fn2y {
} }
/// Register the built-in library. /// Register the built-in library.
impl Engine<'_> { impl Engine {
pub fn register_stdlib(&mut self) { pub fn register_stdlib(&mut self) {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
{ {

View File

@ -10,17 +10,16 @@ use crate::token::Position;
use crate::stdlib::{ use crate::stdlib::{
any::TypeId, any::TypeId,
borrow::Cow,
boxed::Box, boxed::Box,
cmp::Ordering, cmp::Ordering,
collections::HashMap, collections::{hash_map::DefaultHasher, HashMap},
format, format,
hash::{Hash, Hasher},
iter::once, iter::once,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
rc::Rc, rc::Rc,
string::{String, ToString}, string::{String, ToString},
sync::Arc, sync::Arc,
vec,
vec::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<TypeId>,
}
/// A type that holds a library of script-defined functions. /// A type that holds a library of script-defined functions.
/// ///
/// Since script-defined functions have `Dynamic` parameters, functions with the same name /// 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`. /// 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. /// A hashmap containing all compiled functions known to the engine.
pub(crate) functions: Option<HashMap<FnSpec<'e>, Box<FnAny>>>, pub(crate) functions: Option<HashMap<u64, Box<FnAny>>>,
/// A hashmap containing all iterators known to the engine. /// A hashmap containing all iterators known to the engine.
pub(crate) type_iterators: Option<HashMap<TypeId, Box<IteratorFn>>>, pub(crate) type_iterators: Option<HashMap<TypeId, Box<IteratorFn>>>,
@ -250,17 +243,17 @@ pub struct Engine<'e> {
/// Closure for implementing the `print` command. /// Closure for implementing the `print` command.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub(crate) on_print: Option<Box<dyn Fn(&str) + Send + Sync + 'e>>, pub(crate) on_print: Option<Box<dyn Fn(&str) + Send + Sync + 'static>>,
/// Closure for implementing the `print` command. /// Closure for implementing the `print` command.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub(crate) on_print: Option<Box<dyn Fn(&str) + 'e>>, pub(crate) on_print: Option<Box<dyn Fn(&str) + 'static>>,
/// Closure for implementing the `debug` command. /// Closure for implementing the `debug` command.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub(crate) on_debug: Option<Box<dyn Fn(&str) + Send + Sync + 'e>>, pub(crate) on_debug: Option<Box<dyn Fn(&str) + Send + Sync + 'static>>,
/// Closure for implementing the `debug` command. /// Closure for implementing the `debug` command.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub(crate) on_debug: Option<Box<dyn Fn(&str) + 'e>>, pub(crate) on_debug: Option<Box<dyn Fn(&str) + 'static>>,
/// Optimize the AST after compilation. /// Optimize the AST after compilation.
pub(crate) optimization_level: OptimizationLevel, pub(crate) optimization_level: OptimizationLevel,
@ -271,7 +264,7 @@ pub struct Engine<'e> {
pub(crate) max_call_stack_depth: usize, pub(crate) max_call_stack_depth: usize,
} }
impl Default for Engine<'_> { impl Default for Engine {
fn default() -> Self { fn default() -> Self {
// Create the new scripting Engine // Create the new scripting Engine
let mut engine = 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<Item = TypeId>) -> 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` /// Create a new `Engine`
pub fn new() -> Self { pub fn new() -> Self {
Default::default() 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 // Argument must be a string
fn cast_to_string(r: &Dynamic, pos: Position) -> Result<&str, EvalAltResult> { fn cast_to_string(r: &Dynamic, pos: Position) -> Result<&str, EvalAltResult> {
r.as_str() r.as_str()
@ -483,7 +478,9 @@ impl Engine<'_> {
} }
// Search built-in's and external functions // 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 // Run external function
let result = func(args, pos)?; let result = func(args, pos)?;
@ -519,7 +516,7 @@ impl Engine<'_> {
} }
if let Some(prop) = extract_prop_from_setter(fn_name) { 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] { return match arg[0] {
// Map property update // Map property update
@ -1336,10 +1333,8 @@ impl Engine<'_> {
name: &str, name: &str,
) -> bool { ) -> bool {
engine.functions.as_ref().map_or(false, |lib| { engine.functions.as_ref().map_or(false, |lib| {
lib.contains_key(&FnSpec { let fn_spec = calc_fn_spec(name, [TypeId::of::<String>()].iter().cloned());
name: name.into(), lib.contains_key(&fn_spec)
args: vec![TypeId::of::<String>()],
})
}) || fn_lib.map_or(false, |lib| lib.has_function(name, 1)) }) || fn_lib.map_or(false, |lib| lib.has_function(name, 1))
} }

View File

@ -88,13 +88,13 @@ macro_rules! def_anonymous_fn {
def_anonymous_fn!(imp); def_anonymous_fn!(imp);
}; };
(imp $($par:ident),*) => { (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")] #[cfg(feature = "sync")]
type Output = Box<dyn Fn($($par),*) -> Result<RET, EvalAltResult> + Send + Sync + 'e>; type Output = Box<dyn Fn($($par),*) -> Result<RET, EvalAltResult> + Send + Sync>;
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
type Output = Box<dyn Fn($($par),*) -> Result<RET, EvalAltResult> + 'e>; type Output = Box<dyn Fn($($par),*) -> Result<RET, EvalAltResult>>;
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output { fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output {
let name = entry_point.to_string(); let name = entry_point.to_string();

View File

@ -146,7 +146,7 @@ macro_rules! def_register {
FN: Fn($($param),*) -> RET + 'static, FN: Fn($($param),*) -> RET + 'static,
RET: Variant + Clone RET: Variant + Clone
> RegisterFn<FN, ($($mark,)*), RET> for Engine<'_> > RegisterFn<FN, ($($mark,)*), RET> for Engine
{ {
fn register_fn(&mut self, name: &str, f: FN) { fn register_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string(); let fn_name = name.to_string();
@ -184,7 +184,7 @@ macro_rules! def_register {
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
FN: Fn($($param),*) -> Dynamic + 'static, FN: Fn($($param),*) -> Dynamic + 'static,
> RegisterDynamicFn<FN, ($($mark,)*)> for Engine<'_> > RegisterDynamicFn<FN, ($($mark,)*)> for Engine
{ {
fn register_dynamic_fn(&mut self, name: &str, f: FN) { fn register_dynamic_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string(); let fn_name = name.to_string();
@ -221,7 +221,7 @@ macro_rules! def_register {
FN: Fn($($param),*) -> Result<RET, EvalAltResult> + 'static, FN: Fn($($param),*) -> Result<RET, EvalAltResult> + 'static,
RET: Variant + Clone RET: Variant + Clone
> RegisterResultFn<FN, ($($mark,)*), RET> for Engine<'_> > RegisterResultFn<FN, ($($mark,)*), RET> for Engine
{ {
fn register_result_fn(&mut self, name: &str, f: FN) { fn register_result_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string(); let fn_name = name.to_string();

View File

@ -1,7 +1,7 @@
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::engine::{ use crate::engine::{
Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, calc_fn_spec, Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL,
KEYWORD_TYPE_OF, KEYWORD_PRINT, KEYWORD_TYPE_OF,
}; };
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST}; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
@ -50,7 +50,7 @@ struct State<'a> {
/// Collection of constants to use for eager function evaluations. /// Collection of constants to use for eager function evaluations.
constants: Vec<(String, Expr)>, constants: Vec<(String, Expr)>,
/// An `Engine` instance for eager function evaluation. /// An `Engine` instance for eager function evaluation.
engine: &'a Engine<'a>, engine: &'a Engine,
/// Library of script-defined functions. /// Library of script-defined functions.
fn_lib: &'a [(&'a str, usize)], fn_lib: &'a [(&'a str, usize)],
/// Optimization level. /// Optimization level.
@ -60,7 +60,7 @@ struct State<'a> {
impl<'a> State<'a> { impl<'a> State<'a> {
/// Create a new State. /// Create a new State.
pub fn new( pub fn new(
engine: &'a Engine<'a>, engine: &'a Engine,
fn_lib: &'a [(&'a str, usize)], fn_lib: &'a [(&'a str, usize)],
level: OptimizationLevel, level: OptimizationLevel,
) -> Self { ) -> Self {
@ -110,19 +110,14 @@ impl<'a> State<'a> {
/// Call a registered function /// Call a registered function
fn call_fn( fn call_fn(
functions: Option<&HashMap<FnSpec, Box<FnAny>>>, functions: Option<&HashMap<u64, Box<FnAny>>>,
fn_name: &str, fn_name: &str,
args: &mut FnCallArgs, args: &mut FnCallArgs,
pos: Position, pos: Position,
) -> Result<Option<Dynamic>, EvalAltResult> { ) -> Result<Option<Dynamic>, EvalAltResult> {
let spec = FnSpec {
name: fn_name.into(),
args: args.iter().map(|a| a.type_id()).collect(),
};
// Search built-in's and external functions // Search built-in's and external functions
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)) .map(|func| func(args, pos))
.transpose() .transpose()
} }
@ -621,7 +616,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
fn optimize<'a>( fn optimize<'a>(
statements: Vec<Stmt>, statements: Vec<Stmt>,
engine: &Engine<'a>, engine: &Engine,
scope: &Scope, scope: &Scope,
fn_lib: &'a [(&'a str, usize)], fn_lib: &'a [(&'a str, usize)],
level: OptimizationLevel, level: OptimizationLevel,

View File

@ -1759,9 +1759,9 @@ fn parse_fn<'a>(
}) })
} }
pub fn parse_global_expr<'a, 'e>( pub fn parse_global_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
engine: &Engine<'e>, engine: &Engine,
scope: &Scope, scope: &Scope,
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> { ) -> Result<AST, ParseError> {
@ -1841,9 +1841,9 @@ fn parse_global_level<'a>(
} }
/// Run the parser on an input stream, returning an AST. /// Run the parser on an input stream, returning an AST.
pub fn parse<'a, 'e>( pub fn parse<'a>(
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
engine: &Engine<'e>, engine: &Engine,
scope: &Scope, scope: &Scope,
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> { ) -> Result<AST, ParseError> {

View File

@ -81,18 +81,18 @@ fn test_side_effects_command() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_side_effects_print() -> Result<(), EvalAltResult> { fn test_side_effects_print() -> Result<(), EvalAltResult> {
use std::sync::Arc;
use std::sync::RwLock; 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 // Override action of 'print' function
engine.on_print(|s| result.write().unwrap().push_str(s)); 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"); assert_eq!(*result.read().unwrap(), "42");
Ok(()) Ok(())