Encapsulate register_fn_raw parameters into NativeCallContext.

This commit is contained in:
Stephen Chung 2020-10-18 17:02:17 +08:00
parent 58c820785b
commit 82e6dd446a
20 changed files with 368 additions and 265 deletions

View File

@ -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

View File

@ -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<Dynamic, Box<EvalAltResult>>
{
// '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::<FnPtr>(&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)?;
```

View File

@ -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::<i64>(), // type of first parameter
std::any::TypeId::of::<i64>() // 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<T, Box<EvalAltResult>> + 'static`
> `Fn(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>> + '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::<FnPtr>(),
std::any::TypeId::of::<i64>(),
],
|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::<FnPtr>(); // 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])
},
);

View File

@ -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<T> + SendSync + 'static,
func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>>
+ 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<Dynamic> {
) -> Result<Dynamic, Box<EvalAltResult>> {
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<Dynamic> {
) -> Result<Dynamic, Box<EvalAltResult>> {
let fn_def = lib
.get_script_fn(name, args.len(), true)
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?;

View File

@ -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<Dynamic> {
match self {
Self::None | Self::Value(_) => panic!("expecting IndexChainValue::FnCallArgs"),
@ -178,7 +182,6 @@ impl From<Dynamic> 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<T: Into<Dynamic>> From<T> 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<Target<'a>, Box<EvalAltResult>> {
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<Dynamic, Box<EvalAltResult>>,
) -> Result<Dynamic, Box<EvalAltResult>> {
// 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();
}

View File

@ -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()),

View File

@ -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<T> = RefCell<T>;
#[cfg(feature = "sync")]
pub type Locked<T> = RwLock<T>;
/// 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<T: Clone>(value: &mut Shared<T>) -> &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<Module>,
context: NativeCallContext,
this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -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<Dynamic, Box<EvalAltResult>>;
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
/// A general function trail object.
#[cfg(feature = "sync")]
pub type FnAny =
dyn Fn(&Engine, &Module, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
dyn Fn(NativeCallContext, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
/// A standard function that gets an iterator from a type.
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;

View File

@ -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();

View File

@ -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};

View File

@ -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<T> = Result<T, Box<EvalAltResult>>;
pub type FuncInfo = (
String,
FnAccess,
@ -536,10 +537,12 @@ impl Module {
&mut self,
name: impl Into<String>,
arg_types: &[TypeId],
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>>
+ 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<T: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn() -> FuncReturn<T> + SendSync + 'static,
func: impl Fn() -> Result<T, Box<EvalAltResult>> + 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<A: Variant + Clone, T: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn(A) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
func(cast_arg::<A>(&mut args[0])).map(Dynamic::from)
};
let arg_types = [TypeId::of::<A>()];
@ -626,9 +629,9 @@ impl Module {
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from)
};
let arg_types = [TypeId::of::<A>()];
@ -658,7 +661,7 @@ impl Module {
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
self.set_fn_1_mut(make_getter(&name.into()), func)
}
@ -682,9 +685,9 @@ impl Module {
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn(A, B) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]);
@ -719,9 +722,9 @@ impl Module {
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[1]);
let a = &mut args[0].write_lock::<A>().unwrap();
@ -758,7 +761,7 @@ impl Module {
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A, B) -> FuncReturn<()> + SendSync + 'static,
func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + 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<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
if TypeId::of::<A>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
@ -831,9 +834,9 @@ impl Module {
>(
&mut self,
name: impl Into<String>,
func: impl Fn(A, B, C) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]);
@ -874,9 +877,9 @@ impl Module {
>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A, B, C) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[2]);
let c = cast_arg::<C>(&mut args[3]);
let a = &mut args[0].write_lock::<A>().unwrap();
@ -918,7 +921,7 @@ impl Module {
#[inline]
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
&mut self,
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
func: impl Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
if TypeId::of::<A>() == TypeId::of::<Array>() {
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::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]);
let a = &mut args[0].write_lock::<A>().unwrap();
@ -982,8 +985,8 @@ impl Module {
#[inline]
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
setter: impl Fn(&mut A, B, T) -> FuncReturn<()> + SendSync + 'static,
getter: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
setter: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> (u64, u64) {
(
self.set_indexer_get_fn(getter),
@ -1016,9 +1019,9 @@ impl Module {
>(
&mut self,
name: impl Into<String>,
func: impl Fn(A, B, C, D) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]);
@ -1066,9 +1069,9 @@ impl Module {
>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A, B, C, D) -> FuncReturn<T> + SendSync + 'static,
func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]);
let d = cast_arg::<D>(&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<Self> {
pub fn eval_ast_as_new(
mut scope: Scope,
ast: &AST,
engine: &Engine,
) -> Result<Self, Box<EvalAltResult>> {
let mut mods = Imports::new();
// Run the script

View File

@ -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<T: Variant + Clone>(
_engine: &Engine,
_: &Module,
_context: NativeCallContext,
args: &mut [&mut Dynamic],
) -> Result<(), Box<EvalAltResult>> {
let len = *args[1].read_lock::<INT>().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<T: Variant + Clone>(
Ok(())
}
fn map(
engine: &Engine,
lib: &Module,
args: &mut [&mut Dynamic],
) -> Result<Array, Box<EvalAltResult>> {
fn map(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Array, Box<EvalAltResult>> {
let list = args[0].read_lock::<Array>().unwrap();
let mapper = args[1].read_lock::<FnPtr>().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<Array, Box<EvalAltResult>> {
let list = args[0].read_lock::<Array>().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<bool, Box<EvalAltResult>> {
fn some(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<bool, Box<EvalAltResult>> {
let list = args[0].read_lock::<Array>().unwrap();
let filter = args[1].read_lock::<FnPtr>().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<bool, Box<EvalAltResult>> {
fn all(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<bool, Box<EvalAltResult>> {
let list = args[0].read_lock::<Array>().unwrap();
let filter = args[1].read_lock::<FnPtr>().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<Dynamic, Box<EvalAltResult>> {
let list = args[0].read_lock::<Array>().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<Dynamic, Box<EvalAltResult>> {
let list = args[0].read_lock::<Array>().unwrap();
let reducer = args[1].read_lock::<FnPtr>().unwrap();
let initial = args[2].read_lock::<FnPtr>().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<Dynamic, Box<EvalAltResult>> {
let list = args[0].read_lock::<Array>().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<Dynamic, Box<EvalAltResult>> {
let list = args[0].read_lock::<Array>().unwrap();
let reducer = args[1].read_lock::<FnPtr>().unwrap();
let initial = args[2].read_lock::<FnPtr>().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<Dynamic, Box<EvalAltResult>> {
let comparer = args[1].read_lock::<FnPtr>().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<Array, Box<EvalAltResult>> {
let filter = args[1].read_lock::<FnPtr>().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<Array, Box<EvalAltResult>> {
let filter = args[1].read_lock::<FnPtr>().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),
})

View File

@ -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()
}
}

View File

@ -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<T: Variant + Clone>(from: T, to: T) -> FuncReturn<Range<T>> {
fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAltResult>> {
Ok(from..to)
}
@ -34,7 +34,7 @@ where
}
}
fn get_step_range<T>(from: T, to: T, step: T) -> FuncReturn<StepRange<T>>
fn get_step_range<T>(from: T, to: T, step: T) -> Result<StepRange<T>, Box<EvalAltResult>>
where
for<'a> &'a T: Add<&'a T, Output = T>,
T: Variant + Clone + PartialOrd,

View File

@ -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::<ImmutableString>(), TypeId::of::<INT>(), TypeId::of::<char>()],
|_engine: &Engine, _: &Module, args: &mut [&mut Dynamic]| {
|_context, args| {
let len = *args[1].read_lock::<INT>().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();

View File

@ -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<String>,
/// 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() {

View File

@ -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`.

View File

@ -67,14 +67,14 @@ impl EvalContext<'_, '_, '_, '_, '_, '_> {
scope: &mut Scope,
expr: &Expression,
) -> Result<Dynamic, Box<EvalAltResult>> {
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(),
)
}
}

View File

@ -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,

View File

@ -98,12 +98,12 @@ fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
TypeId::of::<FnPtr>(),
TypeId::of::<INT>(),
],
move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
move |context, args| {
let fp = std::mem::take(args[1]).cast::<FnPtr>();
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])
},
);

View File

@ -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<EvalAltResult>> {
engine.register_raw_fn(
"call_with_arg",
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
|context, args| {
let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>();
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<EvalAltResult>> {
engine.register_raw_fn(
"custom_call",
&[TypeId::of::<INT>(), TypeId::of::<FnPtr>()],
|engine: &Engine, module: &Module, args: &mut [&mut Dynamic]| {
|context, args| {
let func = take(args[1]).cast::<FnPtr>();
func.call_dynamic(engine, module, None, [])
func.call_dynamic(context, None, [])
},
);
@ -259,7 +259,6 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
let mut ast = engine.compile(
r#"
let test = "hello";
|x| test + x
"#,
)?;
@ -271,7 +270,7 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
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"));