From 82e6dd446ad30bf470af99a281632a175bddc8d1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 18 Oct 2020 17:02:17 +0800 Subject: [PATCH] Encapsulate register_fn_raw parameters into NativeCallContext. --- RELEASES.md | 1 + doc/src/language/fn-ptr.md | 58 ++++++++++++--- doc/src/rust/register-raw.md | 25 ++++--- src/api.rs | 13 ++-- src/engine.rs | 140 ++++++++++++++++++++++------------- src/fn_call.rs | 8 +- src/fn_native.rs | 48 ++++++++++-- src/fn_register.rs | 5 +- src/lib.rs | 2 +- src/module/mod.rs | 75 ++++++++++--------- src/packages/array_basic.rs | 129 ++++++++++++-------------------- src/packages/fn_basic.rs | 4 + src/packages/iter_basic.rs | 6 +- src/packages/string_more.rs | 13 ++-- src/parser.rs | 35 +++++---- src/settings.rs | 46 ++++++------ src/syntax.rs | 6 +- src/token.rs | 2 +- tests/call_fn.rs | 4 +- tests/closures.rs | 13 ++-- 20 files changed, 368 insertions(+), 265 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 9a7f9d08..1dc6c67d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -10,6 +10,7 @@ Breaking changes * `EvalAltResult::ErrorReadingScriptFile` is removed in favor of the new `EvalAltResult::ErrorSystem`. * `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`. +* `Engine::register_raw_fn` function signature has changed. Enhancements diff --git a/doc/src/language/fn-ptr.md b/doc/src/language/fn-ptr.md index 9e22eb70..5901c23b 100644 --- a/doc/src/language/fn-ptr.md +++ b/doc/src/language/fn-ptr.md @@ -17,10 +17,11 @@ Built-in methods The following standard methods (mostly defined in the [`BasicFnPackage`][packages] but excluded if using a [raw `Engine`]) operate on function pointers: -| Function | Parameter(s) | Description | -| -------------------------- | ------------ | ---------------------------------------------------------------------------- | -| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer | -| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ | +| Function | Parameter(s) | Description | +| ---------------------------------- | ------------ | ---------------------------------------------------------------------------- | +| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer | +| `is_anonymous` method and property | _none_ | does the function pointer refer to an [anonymous function]? | +| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ | Examples @@ -186,16 +187,15 @@ must be used to register the function. Essentially, use the low-level `Engine::register_raw_fn` method to register the function. `FnPtr::call_dynamic` is used to actually call the function pointer, passing to it the -current scripting [`Engine`], collection of script-defined functions, the `this` pointer, -and other necessary arguments. +current _native call context_, the `this` pointer, and other necessary arguments. ```rust -use rhai::{Engine, Module, Dynamic, FnPtr}; +use rhai::{Engine, Module, Dynamic, FnPtr, NativeCallContext}; let mut engine = Engine::new(); // Define Rust function in required low-level API signature -fn call_fn_ptr_with_value(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]) +fn call_fn_ptr_with_value(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result> { // 'args' is guaranteed to contain enough arguments of the correct types @@ -205,7 +205,7 @@ fn call_fn_ptr_with_value(engine: &Engine, lib: &Module, args: &mut [&mut Dynami // Use 'FnPtr::call_dynamic' to call the function pointer. // Beware, private script-defined functions will not be found. - fp.call_dynamic(engine, lib, Some(this_ptr), [value]) + fp.call_dynamic(context, Some(this_ptr), [value]) } // Register a Rust function using the low-level API @@ -218,3 +218,43 @@ engine.register_raw_fn("super_call", call_fn_ptr_with_value ); ``` + + +`NativeCallContext` +------------------ + +`FnPtr::call_dynamic` takes a parameter of type `NativeCallContext` which holds the _native call context_ +of the particular call to a registered Rust function. + +This type is normally provided by the [`Engine`] (e.g. when using `Engine::register_fn_raw`(../rust/register-raw.md)). +However, it may also be manually constructed from a tuple: + +```rust +use rhai::{Engine, FnPtr}; + +let engine = Engine::new(); + +// Compile script to AST +let mut ast = engine.compile( + r#" + let test = "hello"; + |x| test + x // this creates an closure + "#, +)?; + +// Save the closure together with captured variables +let fn_ptr = engine.eval_ast::(&ast)?; + +// Get rid of the script, retaining only functions +ast.retain_functions(|_, _, _| true); + +// Create native call context via a tuple containing the Engine and the +// set of script-defined functions (within the AST) +let context = (&engine, ast.as_ref()).into(); + +// 'f' captures: the engine, the AST, and the closure +let f = move |x: i64| fn_ptr.call_dynamic(context, None, [x.into()]); + +// 'f' can be called like a normal function +let result = f(42)?; +``` diff --git a/doc/src/rust/register-raw.md b/doc/src/rust/register-raw.md index 460df172..8a351c2a 100644 --- a/doc/src/rust/register-raw.md +++ b/doc/src/rust/register-raw.md @@ -18,8 +18,9 @@ The `Engine::register_raw_fn` method is marked _volatile_, meaning that it may b If this is acceptable, then using this method to register a Rust function opens up more opportunities. -In particular, a reference to the current `Engine` instance is passed as an argument so the Rust function -can also use `Engine` facilities (like evaluating a script). +In particular, a the current _native call context_ (in form of the `NativeCallContext` type) is passed as an argument. +`NativeCallContext` exposes the current [`Engine`], among others, so the Rust function can also use [`Engine`] facilities +(such as evaluating a script). ```rust engine.register_raw_fn( @@ -28,7 +29,7 @@ engine.register_raw_fn( std::any::TypeId::of::(), // type of first parameter std::any::TypeId::of::() // type of second parameter ], - |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { // fixed function signature + |context, args| { // fixed function signature // Arguments are guaranteed to be correct in number and of the correct types. // But remember this is Rust, so you can keep only one mutable reference at any one time! @@ -59,17 +60,19 @@ Function Signature The function signature passed to `Engine::register_raw_fn` takes the following form: -> `Fn(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic])` -> `-> Result> + 'static` +> `Fn(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result> + 'static` where: * `T: Variant + Clone` - return type of the function. -* `engine: &Engine` - the current [`Engine`], with all configurations and settings. +* `context: NativeCallContext` - the current _native call context_, which exposes the following: -* `lib: &Module` - the current global library of script-defined functions, as a [`Module`]. - This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`]. + * `context.engine(): &Engine` - the current [`Engine`], with all configurations and settings. + This is sometimes useful for calling a script-defined function within the same evaluation context + using [`Engine::call_fn`][`call_fn`]. + + * `context.namespace(): &Module` - the global namespace of script-defined functions, as a [`Module`]. * `args: &mut [&mut Dynamic]` - a slice containing `&mut` references to [`Dynamic`] values. The slice is guaranteed to contain enough arguments _of the correct types_. @@ -106,7 +109,7 @@ then calls it within the same [`Engine`]. This way, a _callback_ function can b to a native Rust function. ```rust -use rhai::{Engine, Module, Dynamic, FnPtr}; +use rhai::{Engine, FnPtr}; let mut engine = Engine::new(); @@ -118,7 +121,7 @@ engine.register_raw_fn( std::any::TypeId::of::(), std::any::TypeId::of::(), ], - |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { + |context, args| { // 'args' is guaranteed to contain enough arguments of the correct types let fp = std::mem::take(args[1]).cast::(); // 2nd argument - function pointer @@ -127,7 +130,7 @@ engine.register_raw_fn( // Use 'FnPtr::call_dynamic' to call the function pointer. // Beware, private script-defined functions will not be found. - fp.call_dynamic(engine, lib, Some(this_ptr), [value]) + fp.call_dynamic(context, Some(this_ptr), [value]) }, ); diff --git a/src/api.rs b/src/api.rs index 87699672..031e06b7 100644 --- a/src/api.rs +++ b/src/api.rs @@ -3,8 +3,7 @@ use crate::any::{Dynamic, Variant}; use crate::engine::{Engine, EvalContext, Imports, State}; use crate::error::ParseError; -use crate::fn_native::SendSync; -use crate::module::{FuncReturn, Module}; +use crate::fn_native::{NativeCallContext, SendSync}; use crate::optimize::OptimizationLevel; use crate::parser::AST; use crate::result::EvalAltResult; @@ -28,7 +27,7 @@ use crate::{ use crate::fn_register::{RegisterFn, RegisterResultFn}; #[cfg(not(feature = "no_function"))] -use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, StaticVec}; +use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, module::Module, StaticVec}; #[cfg(not(feature = "no_optimize"))] use crate::optimize::optimize_into_ast; @@ -69,7 +68,9 @@ impl Engine { &mut self, name: &str, arg_types: &[TypeId], - func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn + SendSync + 'static, + func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result> + + SendSync + + 'static, ) -> &mut Self { self.global_module.set_raw_fn(name, arg_types, func); self @@ -1622,7 +1623,7 @@ impl Engine { name: &str, mut this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, - ) -> FuncReturn { + ) -> Result> { let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect(); self.call_fn_dynamic_raw(scope, lib.as_ref(), name, &mut this_ptr, args.as_mut()) @@ -1645,7 +1646,7 @@ impl Engine { name: &str, this_ptr: &mut Option<&mut Dynamic>, args: &mut [&mut Dynamic], - ) -> FuncReturn { + ) -> Result> { let fn_def = lib .get_script_fn(name, args.len(), true) .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?; diff --git a/src/engine.rs b/src/engine.rs index 3cb249d6..c570e0c6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,12 +1,12 @@ //! Main module defining the script evaluation `Engine`. -use crate::any::{map_std_type_name, Dynamic, Union}; +use crate::any::{map_std_type_name, Dynamic, Union, Variant}; use crate::fn_call::run_builtin_op_assignment; use crate::fn_native::{Callback, FnPtr, OnVarCallback}; use crate::module::{Module, ModuleRef}; use crate::optimize::OptimizationLevel; use crate::packages::{Package, PackagesCollection, StandardPackage}; -use crate::parser::{Expr, ReturnType, Stmt, INT}; +use crate::parser::{Expr, ReturnType, Stmt}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; @@ -14,8 +14,8 @@ use crate::syntax::CustomSyntax; use crate::token::Position; use crate::{calc_fn_hash, StaticVec}; -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::any::Variant; +#[cfg(not(feature = "no_index"))] +use crate::parser::INT; #[cfg(not(feature = "no_module"))] use crate::module::ModuleResolver; @@ -80,6 +80,7 @@ pub const MAX_CALL_STACK_DEPTH: usize = 12; #[cfg(debug_assertions)] pub const MAX_EXPR_DEPTH: usize = 32; #[cfg(not(feature = "unchecked"))] +#[cfg(not(feature = "no_function"))] #[cfg(debug_assertions)] pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16; @@ -90,6 +91,7 @@ pub const MAX_CALL_STACK_DEPTH: usize = 128; #[cfg(not(debug_assertions))] pub const MAX_EXPR_DEPTH: usize = 128; #[cfg(not(feature = "unchecked"))] +#[cfg(not(feature = "no_function"))] #[cfg(not(debug_assertions))] pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32; @@ -144,6 +146,7 @@ impl IndexChainValue { /// # Panics /// /// Panics if not `IndexChainValue::Value`. + #[cfg(not(feature = "no_index"))] pub fn as_value(self) -> Dynamic { match self { Self::None | Self::FnCallArgs(_) => panic!("expecting IndexChainValue::Value"), @@ -155,6 +158,7 @@ impl IndexChainValue { /// # Panics /// /// Panics if not `IndexChainValue::FnCallArgs`. + #[cfg(not(feature = "no_object"))] pub fn as_fn_call_args(self) -> StaticVec { match self { Self::None | Self::Value(_) => panic!("expecting IndexChainValue::FnCallArgs"), @@ -178,7 +182,6 @@ impl From for IndexChainValue { } /// A type that encapsulates a mutation target for an expression with side effects. -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[derive(Debug)] pub enum Target<'a> { /// The target is a mutable reference to a `Dynamic` value somewhere. @@ -196,7 +199,6 @@ pub enum Target<'a> { StringChar(&'a mut Dynamic, usize, Dynamic), } -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] impl<'a> Target<'a> { /// Is the `Target` a reference pointing to other data? #[allow(dead_code)] @@ -306,6 +308,7 @@ impl<'a> Target<'a> { } } /// Update the value of the `Target`. + #[cfg(any(not(feature = "no_object"), not(feature = "no_index")))] pub fn set_value( &mut self, new_val: (Dynamic, Position), @@ -348,7 +351,6 @@ impl<'a> Target<'a> { } } -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] impl<'a> From<&'a mut Dynamic> for Target<'a> { #[inline(always)] fn from(value: &'a mut Dynamic) -> Self { @@ -364,7 +366,6 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> { } } -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] impl> From for Target<'_> { #[inline(always)] fn from(value: T) -> Self { @@ -414,35 +415,44 @@ pub struct Limits { /// /// Defaults to 16 for debug builds and 128 for non-debug builds. pub max_call_stack_depth: usize, - /// Maximum depth of statements/expressions at global level. + /// Maximum depth of statements/expressions at global level (0 = unlimited). pub max_expr_depth: usize, - /// Maximum depth of statements/expressions in functions. + /// Maximum depth of statements/expressions in functions (0 = unlimited). + /// Not available under `no_function`. + #[cfg(not(feature = "no_function"))] pub max_function_expr_depth: usize, - /// Maximum number of operations allowed to run. + /// Maximum number of operations allowed to run (0 = unlimited). pub max_operations: u64, /// Maximum number of modules allowed to load. + /// Not available under `no_module`. + #[cfg(not(feature = "no_modules"))] pub max_modules: usize, - /// Maximum length of a string. + /// Maximum length of a string (0 = unlimited). pub max_string_size: usize, - /// Maximum length of an array. + /// Maximum length of an array (0 = unlimited). + /// Not available under `no_index`. + #[cfg(not(feature = "no_index"))] pub max_array_size: usize, - /// Maximum number of properties in a map. + /// Maximum number of properties in a map (0 = unlimited). + /// Not available under `no_object`. + #[cfg(not(feature = "no_object"))] pub max_map_size: usize, } /// Context of a script evaluation process. #[derive(Debug)] pub struct EvalContext<'e, 'a, 's, 'm, 't, 'd: 't> { - pub(crate) engine: &'e Engine, + engine: &'e Engine, pub(crate) mods: &'a mut Imports, pub(crate) state: &'s mut State, - pub(crate) lib: &'m Module, + lib: &'m Module, pub(crate) this_ptr: &'t mut Option<&'d mut Dynamic>, - pub(crate) level: usize, + level: usize, } impl<'e, 'a, 's, 'm, 't, 'd> EvalContext<'e, 'a, 's, 'm, 't, 'd> { /// The current `Engine`. + #[inline(always)] pub fn engine(&self) -> &'e Engine { self.engine } @@ -450,14 +460,17 @@ impl<'e, 'a, 's, 'm, 't, 'd> EvalContext<'e, 'a, 's, 'm, 't, 'd> { /// Available under the `internals` feature only. #[cfg(feature = "internals")] #[cfg(not(feature = "no_modules"))] + #[inline(always)] pub fn imports(&self) -> &'a Imports { self.mods } /// The global namespace containing definition of all script-defined functions. + #[inline(always)] pub fn namespace(&self) -> &'m Module { self.lib } /// The current nesting level of function calls. + #[inline(always)] pub fn call_level(&self) -> usize { self.level } @@ -516,7 +529,7 @@ pub struct Engine { /// Max limits. #[cfg(not(feature = "unchecked"))] - pub(crate) limits: Limits, + pub(crate) limits_set: Limits, } impl fmt::Debug for Engine { @@ -662,14 +675,18 @@ impl Engine { }, #[cfg(not(feature = "unchecked"))] - limits: Limits { + limits_set: Limits { max_call_stack_depth: MAX_CALL_STACK_DEPTH, max_expr_depth: MAX_EXPR_DEPTH, + #[cfg(not(feature = "no_function"))] max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, max_operations: 0, + #[cfg(not(feature = "no_module"))] max_modules: usize::MAX, max_string_size: 0, + #[cfg(not(feature = "no_index"))] max_array_size: 0, + #[cfg(not(feature = "no_object"))] max_map_size: 0, }, }; @@ -710,14 +727,18 @@ impl Engine { }, #[cfg(not(feature = "unchecked"))] - limits: Limits { + limits_set: Limits { max_call_stack_depth: MAX_CALL_STACK_DEPTH, max_expr_depth: MAX_EXPR_DEPTH, + #[cfg(not(feature = "no_function"))] max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, max_operations: 0, + #[cfg(not(feature = "no_module"))] max_modules: usize::MAX, max_string_size: 0, + #[cfg(not(feature = "no_index"))] max_array_size: 0, + #[cfg(not(feature = "no_object"))] max_map_size: 0, }, } @@ -1285,7 +1306,7 @@ impl Engine { ) -> Result, Box> { self.inc_operations(state)?; - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + #[cfg(not(feature = "no_index"))] let is_ref = target.is_ref(); let val = target.as_mut(); @@ -1542,7 +1563,7 @@ impl Engine { if func.is_plugin_fn() { func.get_plugin_fn().call(args)?; } else { - func.get_native_fn()(self, lib, args)?; + func.get_native_fn()((self, lib).into(), args)?; } } // Built-in op-assignment function @@ -1954,7 +1975,7 @@ impl Engine { // Guard against too many modules #[cfg(not(feature = "unchecked"))] - if state.modules >= self.limits.max_modules { + if state.modules >= self.max_modules() { return EvalAltResult::ErrorTooManyModules(*_pos).into(); } @@ -2042,8 +2063,19 @@ impl Engine { result: Result>, ) -> Result> { // If no data size limits, just return - if self.limits.max_string_size + self.limits.max_array_size + self.limits.max_map_size == 0 + let mut total = 0; + + total += self.max_string_size(); + #[cfg(not(feature = "no_index"))] { + total += self.max_array_size(); + } + #[cfg(not(feature = "no_object"))] + { + total += self.max_map_size(); + } + + if total == 0 { return result; } @@ -2103,46 +2135,52 @@ impl Engine { // Simply return all errors Err(_) => return result, // String with limit - Ok(Dynamic(Union::Str(_))) if self.limits.max_string_size > 0 => (), + Ok(Dynamic(Union::Str(_))) if self.max_string_size() > 0 => (), // Array with limit #[cfg(not(feature = "no_index"))] - Ok(Dynamic(Union::Array(_))) if self.limits.max_array_size > 0 => (), + Ok(Dynamic(Union::Array(_))) if self.max_array_size() > 0 => (), // Map with limit #[cfg(not(feature = "no_object"))] - Ok(Dynamic(Union::Map(_))) if self.limits.max_map_size > 0 => (), + Ok(Dynamic(Union::Map(_))) if self.max_map_size() > 0 => (), // Everything else is simply returned Ok(_) => return result, }; - let (arr, map, s) = calc_size(result.as_ref().unwrap()); + let (_arr, _map, s) = calc_size(result.as_ref().unwrap()); - if s > self.limits.max_string_size { - EvalAltResult::ErrorDataTooLarge( + if s > self.max_string_size() { + return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - self.limits.max_string_size, + self.max_string_size(), s, Position::none(), ) - .into() - } else if arr > self.limits.max_array_size { - EvalAltResult::ErrorDataTooLarge( - "Size of array".to_string(), - self.limits.max_array_size, - arr, - Position::none(), - ) - .into() - } else if map > self.limits.max_map_size { - EvalAltResult::ErrorDataTooLarge( - "Number of properties in object map".to_string(), - self.limits.max_map_size, - map, - Position::none(), - ) - .into() - } else { - result + .into(); } + + #[cfg(not(feature = "no_index"))] + if _arr > self.max_array_size() { + return EvalAltResult::ErrorDataTooLarge( + "Size of array".to_string(), + self.max_array_size(), + _arr, + Position::none(), + ) + .into(); + } + + #[cfg(not(feature = "no_object"))] + if _map > self.max_map_size() { + return EvalAltResult::ErrorDataTooLarge( + "Number of properties in object map".to_string(), + self.max_map_size(), + _map, + Position::none(), + ) + .into(); + } + + result } /// Check if the number of operations stay within limit. @@ -2152,7 +2190,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] // Guard against too many operations - if self.limits.max_operations > 0 && state.operations > self.limits.max_operations { + if self.max_operations() > 0 && state.operations > self.max_operations() { return EvalAltResult::ErrorTooManyOperations(Position::none()).into(); } diff --git a/src/fn_call.rs b/src/fn_call.rs index 39fbbd7a..b16d35db 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -208,7 +208,7 @@ impl Engine { let result = if func.is_plugin_fn() { func.get_plugin_fn().call(args) } else { - func.get_native_fn()(self, lib, args) + func.get_native_fn()((self, lib).into(), args) }; // Restore the original reference @@ -356,7 +356,7 @@ impl Engine { // Check for stack overflow #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "unchecked"))] - if level > self.limits.max_call_stack_depth { + if level > self.max_call_levels() { return Err(Box::new( EvalAltResult::ErrorStackOverflow(Position::none()), )); @@ -648,7 +648,7 @@ impl Engine { // Check for stack overflow #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "unchecked"))] - if _level > self.limits.max_call_stack_depth { + if _level > self.max_call_levels() { return Err(Box::new( EvalAltResult::ErrorStackOverflow(Position::none()), )); @@ -1196,7 +1196,7 @@ impl Engine { } } - f.get_native_fn()(self, lib, args.as_mut()) + f.get_native_fn()((self, lib).into(), args.as_mut()) } Some(_) => unreachable!(), None if def_val.is_some() => Ok(def_val.unwrap().into()), diff --git a/src/fn_native.rs b/src/fn_native.rs index 7ced25a9..69790b8d 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,7 +1,7 @@ //! Module defining interfaces to native-Rust functions. use crate::any::Dynamic; -use crate::engine::{Engine, EvalContext}; +use crate::engine::{Engine, EvalContext, FN_ANONYMOUS}; use crate::module::Module; use crate::parser::{FnAccess, ScriptFnDef}; use crate::plugin::PluginFunction; @@ -46,6 +46,35 @@ pub type Locked = RefCell; #[cfg(feature = "sync")] pub type Locked = RwLock; +/// Context of a script evaluation process. +#[derive(Debug, Copy, Clone)] +pub struct NativeCallContext<'e, 'm> { + engine: &'e Engine, + lib: &'m Module, +} + +impl<'e, 'm> From<(&'e Engine, &'m Module)> for NativeCallContext<'e, 'm> { + fn from(value: (&'e Engine, &'m Module)) -> Self { + Self { + engine: value.0, + lib: value.1, + } + } +} + +impl<'e, 'm> NativeCallContext<'e, 'm> { + /// The current `Engine`. + #[inline(always)] + pub fn engine(&self) -> &'e Engine { + self.engine + } + /// The global namespace containing definition of all script-defined functions. + #[inline(always)] + pub fn namespace(&self) -> &'m Module { + self.lib + } +} + /// Consume a `Shared` resource and return a mutable reference to the wrapped value. /// If the resource is shared (i.e. has other outstanding references), a cloned copy is used. pub fn shared_make_mut(value: &mut Shared) -> &mut T { @@ -108,6 +137,11 @@ impl FnPtr { pub fn curry(&self) -> &[Dynamic] { self.1.as_ref() } + /// Does this function pointer refer to an anonymous function? + #[inline(always)] + pub fn is_anonymous(&self) -> bool { + self.0.starts_with(FN_ANONYMOUS) + } /// Call the function pointer with curried arguments (if any). /// @@ -121,8 +155,7 @@ impl FnPtr { /// clone them _before_ calling this function. pub fn call_dynamic( &self, - engine: &Engine, - lib: impl AsRef, + context: NativeCallContext, this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, ) -> Result> { @@ -144,10 +177,11 @@ impl FnPtr { args.insert(0, obj); } - engine + context + .engine() .exec_fn_call( &mut Default::default(), - lib.as_ref(), + context.namespace(), fn_name, hash_script, args.as_mut(), @@ -204,11 +238,11 @@ impl TryFrom<&str> for FnPtr { /// A general function trail object. #[cfg(not(feature = "sync"))] -pub type FnAny = dyn Fn(&Engine, &Module, &mut FnCallArgs) -> Result>; +pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> Result>; /// A general function trail object. #[cfg(feature = "sync")] pub type FnAny = - dyn Fn(&Engine, &Module, &mut FnCallArgs) -> Result> + Send + Sync; + dyn Fn(NativeCallContext, &mut FnCallArgs) -> Result> + Send + Sync; /// A standard function that gets an iterator from a type. pub type IteratorFn = fn(Dynamic) -> Box>; diff --git a/src/fn_register.rs b/src/fn_register.rs index d8bbb09a..f394b348 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -4,8 +4,7 @@ use crate::any::{Dynamic, DynamicWriteLock, Variant}; use crate::engine::Engine; -use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; -use crate::module::Module; +use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, NativeCallContext, SendSync}; use crate::parser::FnAccess; use crate::r#unsafe::unsafe_cast_box; use crate::result::EvalAltResult; @@ -127,7 +126,7 @@ macro_rules! make_func { // ^ dereferencing function // ^ argument reference expression(like A, *B, &mut C etc) - Box::new(move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + 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(); diff --git a/src/lib.rs b/src/lib.rs index dd277a73..e2973ae0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,7 +85,7 @@ mod utils; pub use any::Dynamic; pub use engine::{Engine, EvalContext}; pub use error::{ParseError, ParseErrorType}; -pub use fn_native::FnPtr; +pub use fn_native::{FnPtr, NativeCallContext}; pub use fn_register::{RegisterFn, RegisterResultFn}; pub use module::Module; pub use parser::{ImmutableString, AST, INT}; diff --git a/src/module/mod.rs b/src/module/mod.rs index d7cc40d9..dcdfe756 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -2,7 +2,7 @@ use crate::any::{Dynamic, Variant}; use crate::engine::Engine; -use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync}; +use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync}; use crate::fn_register::by_value as cast_arg; use crate::parser::FnAccess; use crate::result::EvalAltResult; @@ -24,7 +24,11 @@ use crate::{ use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET}; #[cfg(not(feature = "no_object"))] -use crate::engine::{make_getter, make_setter, Map}; +use crate::engine::{make_getter, make_setter}; + +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +use crate::engine::Map; use crate::stdlib::{ any::TypeId, @@ -38,9 +42,6 @@ use crate::stdlib::{ vec::Vec, }; -/// Return type of module-level Rust function. -pub type FuncReturn = Result>; - pub type FuncInfo = ( String, FnAccess, @@ -536,10 +537,12 @@ impl Module { &mut self, name: impl Into, arg_types: &[TypeId], - func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn + SendSync + 'static, + func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result> + + SendSync + + 'static, ) -> u64 { - let f = move |engine: &Engine, lib: &Module, args: &mut FnCallArgs| { - func(engine, lib, args).map(Dynamic::from) + let f = move |context: NativeCallContext, args: &mut [&mut Dynamic]| { + func(context, args).map(Dynamic::from) }; self.set_fn( name, @@ -566,9 +569,9 @@ impl Module { pub fn set_fn_0( &mut self, name: impl Into, - func: impl Fn() -> FuncReturn + SendSync + 'static, + func: impl Fn() -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, _: &mut FnCallArgs| func().map(Dynamic::from); + let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from); let arg_types = []; self.set_fn( name, @@ -595,9 +598,9 @@ impl Module { pub fn set_fn_1( &mut self, name: impl Into, - func: impl Fn(A) -> FuncReturn + SendSync + 'static, + func: impl Fn(A) -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + let f = move |_: NativeCallContext, args: &mut FnCallArgs| { func(cast_arg::(&mut args[0])).map(Dynamic::from) }; let arg_types = [TypeId::of::()]; @@ -626,9 +629,9 @@ impl Module { pub fn set_fn_1_mut( &mut self, name: impl Into, - func: impl Fn(&mut A) -> FuncReturn + SendSync + 'static, + func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + let f = move |_: NativeCallContext, args: &mut FnCallArgs| { func(&mut args[0].write_lock::().unwrap()).map(Dynamic::from) }; let arg_types = [TypeId::of::()]; @@ -658,7 +661,7 @@ impl Module { pub fn set_getter_fn( &mut self, name: impl Into, - func: impl Fn(&mut A) -> FuncReturn + SendSync + 'static, + func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> u64 { self.set_fn_1_mut(make_getter(&name.into()), func) } @@ -682,9 +685,9 @@ impl Module { pub fn set_fn_2( &mut self, name: impl Into, - func: impl Fn(A, B) -> FuncReturn + SendSync + 'static, + func: impl Fn(A, B) -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let a = cast_arg::(&mut args[0]); let b = cast_arg::(&mut args[1]); @@ -719,9 +722,9 @@ impl Module { pub fn set_fn_2_mut( &mut self, name: impl Into, - func: impl Fn(&mut A, B) -> FuncReturn + SendSync + 'static, + func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let b = cast_arg::(&mut args[1]); let a = &mut args[0].write_lock::().unwrap(); @@ -758,7 +761,7 @@ impl Module { pub fn set_setter_fn( &mut self, name: impl Into, - func: impl Fn(&mut A, B) -> FuncReturn<()> + SendSync + 'static, + func: impl Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, ) -> u64 { self.set_fn_2_mut(make_setter(&name.into()), func) } @@ -788,7 +791,7 @@ impl Module { #[inline] pub fn set_indexer_get_fn( &mut self, - func: impl Fn(&mut A, B) -> FuncReturn + SendSync + 'static, + func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, ) -> u64 { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); @@ -831,9 +834,9 @@ impl Module { >( &mut self, name: impl Into, - func: impl Fn(A, B, C) -> FuncReturn + SendSync + 'static, + func: impl Fn(A, B, C) -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + 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]); @@ -874,9 +877,9 @@ impl Module { >( &mut self, name: impl Into, - func: impl Fn(&mut A, B, C) -> FuncReturn + SendSync + 'static, + func: impl Fn(&mut A, B, C) -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + 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(); @@ -918,7 +921,7 @@ impl Module { #[inline] pub fn set_indexer_set_fn( &mut self, - func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static, + func: impl Fn(&mut A, B, C) -> Result<(), Box> + SendSync + 'static, ) -> u64 { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); @@ -934,7 +937,7 @@ impl Module { panic!("Cannot register indexer for strings."); } - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + 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(); @@ -982,8 +985,8 @@ impl Module { #[inline] pub fn set_indexer_get_set_fn( &mut self, - getter: impl Fn(&mut A, B) -> FuncReturn + SendSync + 'static, - setter: impl Fn(&mut A, B, T) -> FuncReturn<()> + SendSync + 'static, + getter: impl Fn(&mut A, B) -> Result> + SendSync + 'static, + setter: impl Fn(&mut A, B, T) -> Result<(), Box> + SendSync + 'static, ) -> (u64, u64) { ( self.set_indexer_get_fn(getter), @@ -1016,9 +1019,9 @@ impl Module { >( &mut self, name: impl Into, - func: impl Fn(A, B, C, D) -> FuncReturn + SendSync + 'static, + func: impl Fn(A, B, C, D) -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + 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]); @@ -1066,9 +1069,9 @@ impl Module { >( &mut self, name: impl Into, - func: impl Fn(&mut A, B, C, D) -> FuncReturn + SendSync + 'static, + func: impl Fn(&mut A, B, C, D) -> Result> + SendSync + 'static, ) -> u64 { - let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { + 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]); @@ -1340,7 +1343,11 @@ impl Module { /// # } /// ``` #[cfg(not(feature = "no_module"))] - pub fn eval_ast_as_new(mut scope: Scope, ast: &AST, engine: &Engine) -> FuncReturn { + pub fn eval_ast_as_new( + mut scope: Scope, + ast: &AST, + engine: &Engine, + ) -> Result> { let mut mods = Imports::new(); // Run the script diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 5fa5b103..b42277e7 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -3,8 +3,8 @@ use crate::any::{Dynamic, Variant}; use crate::def_package; -use crate::engine::{Array, Engine}; -use crate::fn_native::FnPtr; +use crate::engine::Array; +use crate::fn_native::{FnPtr, NativeCallContext}; use crate::parser::{ImmutableString, INT}; use crate::plugin::*; use crate::result::EvalAltResult; @@ -239,21 +239,20 @@ mod array_functions { } fn pad( - _engine: &Engine, - _: &Module, + _context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result<(), Box> { let len = *args[1].read_lock::().unwrap(); // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] - if _engine.limits.max_array_size > 0 + if _context.engine().max_array_size() > 0 && len > 0 - && (len as usize) > _engine.limits.max_array_size + && (len as usize) > _context.engine().max_array_size() { return EvalAltResult::ErrorDataTooLarge( "Size of array".to_string(), - _engine.limits.max_array_size, + _context.engine().max_array_size(), len as usize, Position::none(), ) @@ -271,11 +270,7 @@ fn pad( Ok(()) } -fn map( - engine: &Engine, - lib: &Module, - args: &mut [&mut Dynamic], -) -> Result> { +fn map(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result> { let list = args[0].read_lock::().unwrap(); let mapper = args[1].read_lock::().unwrap(); @@ -284,10 +279,10 @@ fn map( for (i, item) in list.iter().enumerate() { array.push( mapper - .call_dynamic(engine, lib, None, [item.clone()]) + .call_dynamic(context, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(_, _) => { - mapper.call_dynamic(engine, lib, None, [item.clone(), (i as INT).into()]) + mapper.call_dynamic(context, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -305,8 +300,7 @@ fn map( } fn filter( - engine: &Engine, - lib: &Module, + context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result> { let list = args[0].read_lock::().unwrap(); @@ -316,10 +310,10 @@ fn filter( for (i, item) in list.iter().enumerate() { if filter - .call_dynamic(engine, lib, None, [item.clone()]) + .call_dynamic(context, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(_, _) => { - filter.call_dynamic(engine, lib, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -340,20 +334,16 @@ fn filter( Ok(array) } -fn some( - engine: &Engine, - lib: &Module, - args: &mut [&mut Dynamic], -) -> Result> { +fn some(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result> { let list = args[0].read_lock::().unwrap(); let filter = args[1].read_lock::().unwrap(); for (i, item) in list.iter().enumerate() { if filter - .call_dynamic(engine, lib, None, [item.clone()]) + .call_dynamic(context, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(_, _) => { - filter.call_dynamic(engine, lib, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -374,20 +364,16 @@ fn some( Ok(false.into()) } -fn all( - engine: &Engine, - lib: &Module, - args: &mut [&mut Dynamic], -) -> Result> { +fn all(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result> { let list = args[0].read_lock::().unwrap(); let filter = args[1].read_lock::().unwrap(); for (i, item) in list.iter().enumerate() { if !filter - .call_dynamic(engine, lib, None, [item.clone()]) + .call_dynamic(context, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(_, _) => { - filter.call_dynamic(engine, lib, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -409,8 +395,7 @@ fn all( } fn reduce( - engine: &Engine, - lib: &Module, + context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result> { let list = args[0].read_lock::().unwrap(); @@ -420,14 +405,11 @@ fn reduce( for (i, item) in list.iter().enumerate() { result = reducer - .call_dynamic(engine, lib, None, [result.clone(), item.clone()]) + .call_dynamic(context, None, [result.clone(), item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic( - engine, - lib, - None, - [result, item.clone(), (i as INT).into()], - ), + EvalAltResult::ErrorFunctionNotFound(_, _) => { + reducer.call_dynamic(context, None, [result, item.clone(), (i as INT).into()]) + } _ => Err(err), }) .map_err(|err| { @@ -443,15 +425,14 @@ fn reduce( } fn reduce_with_initial( - engine: &Engine, - lib: &Module, + context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result> { let list = args[0].read_lock::().unwrap(); let reducer = args[1].read_lock::().unwrap(); let initial = args[2].read_lock::().unwrap(); - let mut result = initial.call_dynamic(engine, lib, None, []).map_err(|err| { + let mut result = initial.call_dynamic(context, None, []).map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), err, @@ -461,14 +442,11 @@ fn reduce_with_initial( for (i, item) in list.iter().enumerate() { result = reducer - .call_dynamic(engine, lib, None, [result.clone(), item.clone()]) + .call_dynamic(context, None, [result.clone(), item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic( - engine, - lib, - None, - [result, item.clone(), (i as INT).into()], - ), + EvalAltResult::ErrorFunctionNotFound(_, _) => { + reducer.call_dynamic(context, None, [result, item.clone(), (i as INT).into()]) + } _ => Err(err), }) .map_err(|err| { @@ -484,8 +462,7 @@ fn reduce_with_initial( } fn reduce_rev( - engine: &Engine, - lib: &Module, + context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result> { let list = args[0].read_lock::().unwrap(); @@ -495,14 +472,11 @@ fn reduce_rev( for (i, item) in list.iter().enumerate().rev() { result = reducer - .call_dynamic(engine, lib, None, [result.clone(), item.clone()]) + .call_dynamic(context, None, [result.clone(), item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic( - engine, - lib, - None, - [result, item.clone(), (i as INT).into()], - ), + EvalAltResult::ErrorFunctionNotFound(_, _) => { + reducer.call_dynamic(context, None, [result, item.clone(), (i as INT).into()]) + } _ => Err(err), }) .map_err(|err| { @@ -518,15 +492,14 @@ fn reduce_rev( } fn reduce_rev_with_initial( - engine: &Engine, - lib: &Module, + context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result> { let list = args[0].read_lock::().unwrap(); let reducer = args[1].read_lock::().unwrap(); let initial = args[2].read_lock::().unwrap(); - let mut result = initial.call_dynamic(engine, lib, None, []).map_err(|err| { + let mut result = initial.call_dynamic(context, None, []).map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), err, @@ -536,14 +509,11 @@ fn reduce_rev_with_initial( for (i, item) in list.iter().enumerate().rev() { result = reducer - .call_dynamic(engine, lib, None, [result.clone(), item.clone()]) + .call_dynamic(context, None, [result.clone(), item.clone()]) .or_else(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic( - engine, - lib, - None, - [result, item.clone(), (i as INT).into()], - ), + EvalAltResult::ErrorFunctionNotFound(_, _) => { + reducer.call_dynamic(context, None, [result, item.clone(), (i as INT).into()]) + } _ => Err(err), }) .map_err(|err| { @@ -559,8 +529,7 @@ fn reduce_rev_with_initial( } fn sort( - engine: &Engine, - lib: &Module, + context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result> { let comparer = args[1].read_lock::().unwrap().clone(); @@ -568,7 +537,7 @@ fn sort( list.sort_by(|x, y| { comparer - .call_dynamic(engine, lib, None, [x.clone(), y.clone()]) + .call_dynamic(context, None, [x.clone(), y.clone()]) .ok() .and_then(|v| v.as_int().ok()) .map(|v| { @@ -598,8 +567,7 @@ fn sort( } fn drain( - engine: &Engine, - lib: &Module, + context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result> { let filter = args[1].read_lock::().unwrap().clone(); @@ -613,10 +581,10 @@ fn drain( i -= 1; if filter - .call_dynamic(engine, lib, None, [list[i].clone()]) + .call_dynamic(context, None, [list[i].clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(_, _) => { - filter.call_dynamic(engine, lib, None, [list[i].clone(), (i as INT).into()]) + filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()]) } _ => Err(err), }) @@ -638,8 +606,7 @@ fn drain( } fn retain( - engine: &Engine, - lib: &Module, + context: NativeCallContext, args: &mut [&mut Dynamic], ) -> Result> { let filter = args[1].read_lock::().unwrap().clone(); @@ -653,10 +620,10 @@ fn retain( i -= 1; if !filter - .call_dynamic(engine, lib, None, [list[i].clone()]) + .call_dynamic(context, None, [list[i].clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(_, _) => { - filter.call_dynamic(engine, lib, None, [list[i].clone(), (i as INT).into()]) + filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()]) } _ => Err(err), }) diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 495a08c3..6816407d 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -12,4 +12,8 @@ mod fn_ptr_functions { pub fn name(f: &mut FnPtr) -> ImmutableString { f.get_fn_name().clone() } + #[rhai_fn(name = "is_anonymous", get = "is_anonymous")] + pub fn is_anonymous(f: &mut FnPtr) -> bool { + f.is_anonymous() + } } diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index c4832c95..0b3dead7 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,11 +1,11 @@ use crate::any::Variant; use crate::def_package; -use crate::module::FuncReturn; use crate::parser::INT; +use crate::result::EvalAltResult; use crate::stdlib::ops::{Add, Range}; -fn get_range(from: T, to: T) -> FuncReturn> { +fn get_range(from: T, to: T) -> Result, Box> { Ok(from..to) } @@ -34,7 +34,7 @@ where } } -fn get_step_range(from: T, to: T, step: T) -> FuncReturn> +fn get_step_range(from: T, to: T, step: T) -> Result, Box> where for<'a> &'a T: Add<&'a T, Output = T>, T: Variant + Clone + PartialOrd, diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index e3b78292..1a311b5d 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -2,7 +2,6 @@ use crate::any::Dynamic; use crate::def_package; -use crate::engine::Engine; use crate::fn_native::FnPtr; use crate::parser::{ImmutableString, INT}; use crate::plugin::*; @@ -62,15 +61,17 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str lib.set_raw_fn( "pad", &[TypeId::of::(), TypeId::of::(), TypeId::of::()], - |_engine: &Engine, _: &Module, args: &mut [&mut Dynamic]| { + |_context, args| { let len = *args[1].read_lock::().unwrap(); // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] - if _engine.limits.max_string_size > 0 && len > 0 && (len as usize) > _engine.limits.max_string_size { + if _context.engine().max_string_size() > 0 && len > 0 + && (len as usize) > _context.engine().max_string_size() + { return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - _engine.limits.max_string_size, + _context.engine().max_string_size(), len as usize, Position::none(), ).into(); @@ -90,10 +91,10 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str } #[cfg(not(feature = "unchecked"))] - if _engine.limits.max_string_size > 0 && s.len() > _engine.limits.max_string_size { + if _context.engine().max_string_size() > 0 && s.len() > _context.engine().max_string_size() { return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - _engine.limits.max_string_size, + _context.engine().max_string_size(), s.len(), Position::none(), ).into(); diff --git a/src/parser.rs b/src/parser.rs index a26ec543..404d1ffd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,9 +1,7 @@ //! Main module defining the lexer and parser. use crate::any::{Dynamic, Union}; -use crate::engine::{ - Engine, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT, -}; +use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; use crate::error::{LexError, ParseError, ParseErrorType}; use crate::fn_native::{FnPtr, Shared}; use crate::module::{Module, ModuleRef}; @@ -18,7 +16,7 @@ use crate::{calc_fn_hash, StaticVec}; use crate::engine::Array; #[cfg(not(feature = "no_object"))] -use crate::engine::{make_getter, make_setter, Map}; +use crate::engine::{make_getter, make_setter, Map, KEYWORD_EVAL, KEYWORD_FN_PTR}; #[cfg(not(feature = "no_function"))] use crate::engine::{FN_ANONYMOUS, KEYWORD_FN_PTR_CURRY}; @@ -603,13 +601,15 @@ struct ParseState<'e> { /// All consequent calls to `access_var` will not be affected #[cfg(not(feature = "no_closure"))] allow_capture: bool, - /// Encapsulates a local stack with variable names to simulate an actual runtime scope. + /// Encapsulates a local stack with imported module names. + #[cfg(not(feature = "no_module"))] modules: Vec, /// Maximum levels of expression nesting. #[cfg(not(feature = "unchecked"))] max_expr_depth: usize, /// Maximum levels of expression nesting in functions. #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_function"))] max_function_expr_depth: usize, } @@ -619,19 +619,23 @@ impl<'e> ParseState<'e> { pub fn new( engine: &'e Engine, #[cfg(not(feature = "unchecked"))] max_expr_depth: usize, - #[cfg(not(feature = "unchecked"))] max_function_expr_depth: usize, + #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_function"))] + max_function_expr_depth: usize, ) -> Self { Self { engine, #[cfg(not(feature = "unchecked"))] max_expr_depth, #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_function"))] max_function_expr_depth, #[cfg(not(feature = "no_closure"))] externals: Default::default(), #[cfg(not(feature = "no_closure"))] allow_capture: true, stack: Default::default(), + #[cfg(not(feature = "no_module"))] modules: Default::default(), } } @@ -1696,11 +1700,10 @@ fn parse_array_literal( while !input.peek().unwrap().0.is_eof() { #[cfg(not(feature = "unchecked"))] - if state.engine.limits.max_array_size > 0 && arr.len() >= state.engine.limits.max_array_size - { + if state.engine.max_array_size() > 0 && arr.len() >= state.engine.max_array_size() { return Err(PERR::LiteralTooLarge( "Size of array literal".to_string(), - state.engine.limits.max_array_size, + state.engine.max_array_size(), ) .into_err(input.peek().unwrap().1)); } @@ -1804,10 +1807,10 @@ fn parse_map_literal( }; #[cfg(not(feature = "unchecked"))] - if state.engine.limits.max_map_size > 0 && map.len() >= state.engine.limits.max_map_size { + if state.engine.max_map_size() > 0 && map.len() >= state.engine.max_map_size() { return Err(PERR::LiteralTooLarge( "Number of properties in object map literal".to_string(), - state.engine.limits.max_map_size, + state.engine.max_map_size(), ) .into_err(input.peek().unwrap().1)); } @@ -3599,9 +3602,10 @@ impl Engine { let mut state = ParseState::new( self, #[cfg(not(feature = "unchecked"))] - self.limits.max_expr_depth, + self.max_expr_depth(), #[cfg(not(feature = "unchecked"))] - self.limits.max_function_expr_depth, + #[cfg(not(feature = "no_function"))] + self.max_function_expr_depth(), ); let settings = ParseSettings { @@ -3646,9 +3650,10 @@ impl Engine { let mut state = ParseState::new( self, #[cfg(not(feature = "unchecked"))] - self.limits.max_expr_depth, + self.max_expr_depth(), #[cfg(not(feature = "unchecked"))] - self.limits.max_function_expr_depth, + #[cfg(not(feature = "no_function"))] + self.max_function_expr_depth(), ); while !input.peek().unwrap().0.is_eof() { diff --git a/src/settings.rs b/src/settings.rs index c10f5aa7..c72fe2d8 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -53,7 +53,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self { - self.limits.max_call_stack_depth = levels; + self.limits_set.max_call_stack_depth = levels; self } @@ -61,7 +61,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_call_levels(&self) -> usize { - self.limits.max_call_stack_depth + self.limits_set.max_call_stack_depth } /// Set the maximum number of operations allowed for a script to run to avoid @@ -69,7 +69,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn set_max_operations(&mut self, operations: u64) -> &mut Self { - self.limits.max_operations = if operations == u64::MAX { + self.limits_set.max_operations = if operations == u64::MAX { 0 } else { operations @@ -81,14 +81,14 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_operations(&self) -> u64 { - self.limits.max_operations + self.limits_set.max_operations } /// Set the maximum number of imported modules allowed for a script. #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn set_max_modules(&mut self, modules: usize) -> &mut Self { - self.limits.max_modules = modules; + self.limits_set.max_modules = modules; self } @@ -96,7 +96,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_modules(&self) -> usize { - self.limits.max_modules + self.limits_set.max_modules } /// Set the depth limits for expressions (0 for unlimited). @@ -105,18 +105,21 @@ impl Engine { pub fn set_max_expr_depths( &mut self, max_expr_depth: usize, - max_function_expr_depth: usize, + #[cfg(not(feature = "no_function"))] max_function_expr_depth: usize, ) -> &mut Self { - self.limits.max_expr_depth = if max_expr_depth == usize::MAX { + self.limits_set.max_expr_depth = if max_expr_depth == usize::MAX { 0 } else { max_expr_depth }; - self.limits.max_function_expr_depth = if max_function_expr_depth == usize::MAX { - 0 - } else { - max_function_expr_depth - }; + #[cfg(not(feature = "no_function"))] + { + self.limits_set.max_function_expr_depth = if max_function_expr_depth == usize::MAX { + 0 + } else { + max_function_expr_depth + }; + } self } @@ -124,21 +127,22 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_expr_depth(&self) -> usize { - self.limits.max_expr_depth + self.limits_set.max_expr_depth } /// The depth limit for expressions in functions (0 for unlimited). #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn max_function_expr_depth(&self) -> usize { - self.limits.max_function_expr_depth + self.limits_set.max_function_expr_depth } /// Set the maximum length of strings (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self { - self.limits.max_string_size = if max_size == usize::MAX { 0 } else { max_size }; + self.limits_set.max_string_size = if max_size == usize::MAX { 0 } else { max_size }; self } @@ -146,7 +150,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_string_size(&self) -> usize { - self.limits.max_string_size + self.limits_set.max_string_size } /// Set the maximum length of arrays (0 for unlimited). @@ -154,7 +158,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self { - self.limits.max_array_size = if max_size == usize::MAX { 0 } else { max_size }; + self.limits_set.max_array_size = if max_size == usize::MAX { 0 } else { max_size }; self } @@ -163,7 +167,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] #[inline(always)] pub fn max_array_size(&self) -> usize { - self.limits.max_array_size + self.limits_set.max_array_size } /// Set the maximum length of object maps (0 for unlimited). @@ -171,7 +175,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] #[inline(always)] pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self { - self.limits.max_map_size = if max_size == usize::MAX { 0 } else { max_size }; + self.limits_set.max_map_size = if max_size == usize::MAX { 0 } else { max_size }; self } @@ -180,7 +184,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] #[inline(always)] pub fn max_map_size(&self) -> usize { - self.limits.max_map_size + self.limits_set.max_map_size } /// Set the module resolution service used by the `Engine`. diff --git a/src/syntax.rs b/src/syntax.rs index 5e827105..08b102b7 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -67,14 +67,14 @@ impl EvalContext<'_, '_, '_, '_, '_, '_> { scope: &mut Scope, expr: &Expression, ) -> Result> { - self.engine.eval_expr( + self.engine().eval_expr( scope, self.mods, self.state, - self.lib, + self.namespace(), self.this_ptr, expr.expr(), - self.level, + self.call_level(), ) } } diff --git a/src/token.rs b/src/token.rs index 92f10da8..418765d6 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1747,7 +1747,7 @@ pub fn lex<'a, 'e>( engine, state: TokenizeState { #[cfg(not(feature = "unchecked"))] - max_string_size: engine.limits.max_string_size, + max_string_size: engine.limits_set.max_string_size, #[cfg(feature = "unchecked")] max_string_size: 0, non_unary: false, diff --git a/tests/call_fn.rs b/tests/call_fn.rs index 5d22abd1..4d4e3552 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -98,12 +98,12 @@ fn test_fn_ptr_raw() -> Result<(), Box> { TypeId::of::(), TypeId::of::(), ], - move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { + move |context, args| { let fp = std::mem::take(args[1]).cast::(); let value = args[2].clone(); let this_ptr = args.get_mut(0).unwrap(); - fp.call_dynamic(engine, lib, Some(this_ptr), [value]) + fp.call_dynamic(context, Some(this_ptr), [value]) }, ); diff --git a/tests/closures.rs b/tests/closures.rs index 6f5de9e7..4f5f1b17 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "no_function"))] -use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Module, ParseErrorType, RegisterFn, Scope, INT}; +use rhai::{Engine, EvalAltResult, FnPtr, ParseErrorType, RegisterFn, Scope, INT}; use std::any::TypeId; use std::cell::RefCell; use std::mem::take; @@ -16,9 +16,9 @@ fn test_fn_ptr_curry_call() -> Result<(), Box> { engine.register_raw_fn( "call_with_arg", &[TypeId::of::(), TypeId::of::()], - |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { + |context, args| { let fn_ptr = std::mem::take(args[0]).cast::(); - fn_ptr.call_dynamic(engine, lib, None, [std::mem::take(args[1])]) + fn_ptr.call_dynamic(context, None, [std::mem::take(args[1])]) }, ); @@ -135,10 +135,10 @@ fn test_closures() -> Result<(), Box> { engine.register_raw_fn( "custom_call", &[TypeId::of::(), TypeId::of::()], - |engine: &Engine, module: &Module, args: &mut [&mut Dynamic]| { + |context, args| { let func = take(args[1]).cast::(); - func.call_dynamic(engine, module, None, []) + func.call_dynamic(context, None, []) }, ); @@ -259,7 +259,6 @@ fn test_closures_external() -> Result<(), Box> { let mut ast = engine.compile( r#" let test = "hello"; - |x| test + x "#, )?; @@ -271,7 +270,7 @@ fn test_closures_external() -> Result<(), Box> { ast.retain_functions(|_, _, _| true); // Closure 'f' captures: the engine, the AST, and the curried function pointer - let f = move |x: INT| fn_ptr.call_dynamic(&engine, ast, None, [x.into()]); + let f = move |x: INT| fn_ptr.call_dynamic((&engine, ast.as_ref()).into(), None, [x.into()]); assert_eq!(f(42)?.as_str(), Ok("hello42"));