Change call_fn_raw to call_fn_with_options.
This commit is contained in:
parent
3feff3618a
commit
d151c87687
12
CHANGELOG.md
12
CHANGELOG.md
@ -4,17 +4,27 @@ Rhai Release Notes
|
||||
Version 1.12.0
|
||||
==============
|
||||
|
||||
Net features
|
||||
------------
|
||||
|
||||
### `Engine::call_fn_with_options`
|
||||
|
||||
* `Engine::call_fn_raw` is deprecated in favor of `Engine::call_fn_with_options` which allows setting options for the function call.
|
||||
* The options are for future-proofing the API.
|
||||
* In this version, it gains the ability to set the value of the _custom state_ (accessible via `NativeCallContext::tag`) for a function evaluation, overriding `Engine::set_default_tag`.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* `CallableFunction` is exported under `internals`.
|
||||
* The `TypeBuilder` type and `CustomType` trait are no longer marked as volatile.
|
||||
* `FuncArgs` is also implemented for arrays.
|
||||
|
||||
|
||||
Version 1.11.0
|
||||
==============
|
||||
|
||||
Speed Improvements
|
||||
Speed improvements
|
||||
------------------
|
||||
|
||||
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
||||
|
@ -8,7 +8,7 @@ pub fn main() {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn main() {
|
||||
use rhai::{Dynamic, Engine, Map, Scope, AST};
|
||||
use rhai::{CallFnOptions, Dynamic, Engine, ImmutableString, Map, Scope, AST};
|
||||
use std::io::{stdin, stdout, Write};
|
||||
|
||||
const SCRIPT_FILE: &str = "event_handler_js/script.rhai";
|
||||
@ -98,7 +98,12 @@ pub fn main() {
|
||||
println!();
|
||||
|
||||
// Run the 'init' function to initialize the state, retaining variables.
|
||||
let result = engine.call_fn_raw(&mut scope, &ast, false, true, "init", Some(&mut states), []);
|
||||
|
||||
let options = CallFnOptions::new()
|
||||
.eval_ast(false)
|
||||
.bind_this_ptr(&mut states);
|
||||
|
||||
let result = engine.call_fn_with_options::<()>(options, &mut scope, &ast, "init", ());
|
||||
|
||||
if let Err(err) = result {
|
||||
eprintln!("! {err}")
|
||||
@ -124,7 +129,7 @@ pub fn main() {
|
||||
let mut fields = input.trim().splitn(2, ' ');
|
||||
|
||||
let event = fields.next().expect("event").trim();
|
||||
let arg = fields.next().unwrap_or("");
|
||||
let arg = fields.next().unwrap_or("").to_string();
|
||||
|
||||
// Process event
|
||||
match event {
|
||||
@ -146,10 +151,11 @@ pub fn main() {
|
||||
let engine = &handler.engine;
|
||||
let scope = &mut handler.scope;
|
||||
let ast = &handler.ast;
|
||||
let this_ptr = Some(&mut handler.states);
|
||||
let options = CallFnOptions::new()
|
||||
.eval_ast(false)
|
||||
.bind_this_ptr(&mut handler.states);
|
||||
|
||||
let result =
|
||||
engine.call_fn_raw(scope, ast, false, true, event, this_ptr, [arg.into()]);
|
||||
let result = engine.call_fn_with_options::<()>(options, scope, ast, event, (arg,));
|
||||
|
||||
if let Err(err) = result {
|
||||
eprintln!("! {err}")
|
||||
|
@ -7,7 +7,7 @@ pub fn main() {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub fn main() {
|
||||
use rhai::{Dynamic, Engine, Scope, AST};
|
||||
use rhai::{CallFnOptions, Dynamic, Engine, Scope, AST};
|
||||
use std::io::{stdin, stdout, Write};
|
||||
|
||||
const SCRIPT_FILE: &str = "event_handler_main/script.rhai";
|
||||
@ -86,7 +86,9 @@ pub fn main() {
|
||||
println!();
|
||||
|
||||
// Run the 'init' function to initialize the state, retaining variables.
|
||||
let result = engine.call_fn_raw(&mut scope, &ast, false, false, "init", None, []);
|
||||
let options = CallFnOptions::new().eval_ast(false).rewind_scope(false);
|
||||
|
||||
let result = engine.call_fn_with_options::<()>(options, &mut scope, &ast, "init", ());
|
||||
|
||||
if let Err(err) = result {
|
||||
eprintln!("! {err}")
|
||||
@ -107,7 +109,7 @@ pub fn main() {
|
||||
let mut fields = input.trim().splitn(2, ' ');
|
||||
|
||||
let event = fields.next().expect("event").trim();
|
||||
let arg = fields.next().unwrap_or("");
|
||||
let arg = fields.next().unwrap_or("").to_string();
|
||||
|
||||
// Process event
|
||||
match event {
|
||||
@ -124,7 +126,7 @@ pub fn main() {
|
||||
let scope = &mut handler.scope;
|
||||
let ast = &handler.ast;
|
||||
|
||||
let result = engine.call_fn::<()>(scope, ast, event, (arg.to_string(),));
|
||||
let result = engine.call_fn::<()>(scope, ast, event, (arg,));
|
||||
|
||||
if let Err(err) = result {
|
||||
eprintln!("! {err}")
|
||||
|
@ -121,7 +121,7 @@ pub fn main() {
|
||||
let mut fields = input.trim().splitn(2, ' ');
|
||||
|
||||
let event = fields.next().expect("event").trim();
|
||||
let arg = fields.next().unwrap_or("");
|
||||
let arg = fields.next().unwrap_or("").to_string();
|
||||
|
||||
// Process event
|
||||
match event {
|
||||
@ -138,7 +138,7 @@ pub fn main() {
|
||||
let scope = &mut handler.scope;
|
||||
let ast = &handler.ast;
|
||||
|
||||
let result = engine.call_fn::<()>(scope, ast, event, (arg.to_string(),));
|
||||
let result = engine.call_fn::<()>(scope, ast, event, (arg,));
|
||||
|
||||
if let Err(err) = result {
|
||||
eprintln!("! {err}")
|
||||
|
@ -8,9 +8,70 @@ use crate::{
|
||||
reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST,
|
||||
ERR,
|
||||
};
|
||||
use std::any::{type_name, TypeId};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
any::{type_name, TypeId},
|
||||
mem,
|
||||
};
|
||||
|
||||
/// Options for calling a script-defined function via [`Engine::call_fn_with_options`].
|
||||
#[derive(Debug, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub struct CallFnOptions<'t> {
|
||||
/// A value for binding to the `this` pointer (if any).
|
||||
pub this_ptr: Option<&'t mut Dynamic>,
|
||||
/// The custom state of this evaluation run (if any), overrides [`Engine::default_tag`].
|
||||
pub tag: Option<Dynamic>,
|
||||
/// Evaluate the [`AST`] to load necessary modules before calling the function? Default `true`.
|
||||
pub eval_ast: bool,
|
||||
/// Rewind the [`Scope`] after the function call? Default `true`.
|
||||
pub rewind_scope: bool,
|
||||
}
|
||||
|
||||
impl Default for CallFnOptions<'_> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CallFnOptions<'a> {
|
||||
/// Create a default [`CallFnOptions`].
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
this_ptr: None,
|
||||
tag: None,
|
||||
eval_ast: true,
|
||||
rewind_scope: true,
|
||||
}
|
||||
}
|
||||
/// Bind to the `this` pointer.
|
||||
#[inline(always)]
|
||||
pub fn bind_this_ptr(mut self, value: &'a mut Dynamic) -> Self {
|
||||
self.this_ptr = Some(value);
|
||||
self
|
||||
}
|
||||
/// Set the custom state of this evaluation run (if any).
|
||||
#[inline(always)]
|
||||
pub fn with_tag(mut self, value: impl Variant + Clone) -> Self {
|
||||
self.tag = Some(Dynamic::from(value));
|
||||
self
|
||||
}
|
||||
/// Set whether to evaluate the [`AST`] to load necessary modules before calling the function.
|
||||
#[inline(always)]
|
||||
pub const fn eval_ast(mut self, value: bool) -> Self {
|
||||
self.eval_ast = value;
|
||||
self
|
||||
}
|
||||
/// Set whether to rewind the [`Scope`] after the function call.
|
||||
#[inline(always)]
|
||||
pub const fn rewind_scope(mut self, value: bool) -> Self {
|
||||
self.rewind_scope = value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
/// Call a script function defined in an [`AST`] with multiple arguments.
|
||||
@ -19,15 +80,12 @@ impl Engine {
|
||||
///
|
||||
/// The [`AST`] is evaluated before calling the function.
|
||||
/// This allows a script to load the necessary modules.
|
||||
/// This is usually desired. If not, a specialized [`AST`] can be prepared that contains only
|
||||
/// function definitions without any body script via [`AST::clear_statements`].
|
||||
/// This is usually desired. If not, use [`call_fn_with_options`] instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # #[cfg(not(feature = "no_function"))]
|
||||
/// # {
|
||||
/// use rhai::{Engine, Scope};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -51,30 +109,83 @@ impl Engine {
|
||||
///
|
||||
/// let result = engine.call_fn::<i64>(&mut scope, &ast, "bar", () )?;
|
||||
/// assert_eq!(result, 21);
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn call_fn<T: Variant + Clone>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
name: impl AsRef<str>,
|
||||
args: impl FuncArgs,
|
||||
) -> RhaiResultOf<T> {
|
||||
self.call_fn_with_options(Default::default(), scope, ast, name, args)
|
||||
}
|
||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
///
|
||||
/// Options are provided via the [`CallFnOptions`] type.
|
||||
/// This is an advanced API.
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Scope, Dynamic, CallFnOptions};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
///
|
||||
/// let ast = engine.compile("
|
||||
/// fn action(x) { this += x; } // function using 'this' pointer
|
||||
/// fn decl(x) { let hello = x; } // declaring variables
|
||||
/// ")?;
|
||||
///
|
||||
/// let mut scope = Scope::new();
|
||||
/// scope.push("foo", 42_i64);
|
||||
///
|
||||
/// // Binding the 'this' pointer
|
||||
/// let mut value = 1_i64.into();
|
||||
/// let options = CallFnOptions::new().bind_this_ptr(&mut value);
|
||||
///
|
||||
/// engine.call_fn_with_options(options, &mut scope, &ast, "action", ( 41_i64, ))?;
|
||||
/// assert_eq!(value.as_int().unwrap(), 42);
|
||||
///
|
||||
/// // Do not rewind scope
|
||||
/// let options = CallFnOptions::default().rewind_scope(false);
|
||||
///
|
||||
/// engine.call_fn_with_options(options, &mut scope, &ast, "decl", ( 42_i64, ))?;
|
||||
/// assert_eq!(scope.get_value::<i64>("hello").unwrap(), 42);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn call_fn_with_options<T: Variant + Clone>(
|
||||
&self,
|
||||
options: CallFnOptions,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
name: impl AsRef<str>,
|
||||
args: impl FuncArgs,
|
||||
) -> RhaiResultOf<T> {
|
||||
let mut arg_values = StaticVec::new_const();
|
||||
args.parse(&mut arg_values);
|
||||
|
||||
let result = self.call_fn_raw(scope, ast, true, true, name, None, arg_values)?;
|
||||
let result = self._call_fn(
|
||||
options,
|
||||
scope,
|
||||
&mut GlobalRuntimeState::new(self),
|
||||
&mut Caches::new(),
|
||||
ast,
|
||||
name.as_ref(),
|
||||
arg_values.as_mut(),
|
||||
)?;
|
||||
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return Ok(reify!(result => T));
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||
return Ok(reify!(() => T));
|
||||
}
|
||||
|
||||
// Cast return type
|
||||
let typ = self.map_type_name(result.type_name());
|
||||
@ -86,120 +197,6 @@ impl Engine {
|
||||
}
|
||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
///
|
||||
/// The following options are available:
|
||||
///
|
||||
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
|
||||
/// * whether to rewind the [`Scope`] after the function call
|
||||
/// * a value for binding to the `this` pointer (if any)
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
||||
/// This is to avoid unnecessarily cloning the arguments.
|
||||
///
|
||||
/// Do not use the arguments after this call. If they are needed afterwards, clone them _before_
|
||||
/// calling this function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # #[cfg(not(feature = "no_function"))]
|
||||
/// # {
|
||||
/// use rhai::{Engine, Scope, Dynamic};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
///
|
||||
/// let ast = engine.compile("
|
||||
/// fn add(x, y) { len(x) + y + foo }
|
||||
/// fn add1(x) { len(x) + 1 + foo }
|
||||
/// fn bar() { foo/2 }
|
||||
/// fn action(x) { this += x; } // function using 'this' pointer
|
||||
/// fn decl(x) { let hello = x; } // declaring variables
|
||||
/// ")?;
|
||||
///
|
||||
/// let mut scope = Scope::new();
|
||||
/// scope.push("foo", 42_i64);
|
||||
///
|
||||
/// // Call the script-defined function
|
||||
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "add", None, [ "abc".into(), 123_i64.into() ])?;
|
||||
/// // ^^^^ no 'this' pointer
|
||||
/// assert_eq!(result.cast::<i64>(), 168);
|
||||
///
|
||||
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "add1", None, [ "abc".into() ])?;
|
||||
/// assert_eq!(result.cast::<i64>(), 46);
|
||||
///
|
||||
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "bar", None, [])?;
|
||||
/// assert_eq!(result.cast::<i64>(), 21);
|
||||
///
|
||||
/// let mut value = 1_i64.into();
|
||||
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "action", Some(&mut value), [ 41_i64.into() ])?;
|
||||
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
||||
/// assert_eq!(value.as_int().unwrap(), 42);
|
||||
///
|
||||
/// engine.call_fn_raw(&mut scope, &ast, true, false, "decl", None, [ 42_i64.into() ])?;
|
||||
/// // ^^^^^ do not rewind scope
|
||||
/// assert_eq!(scope.get_value::<i64>("hello").unwrap(), 42);
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn call_fn_raw(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
eval_ast: bool,
|
||||
rewind_scope: bool,
|
||||
name: impl AsRef<str>,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> RhaiResult {
|
||||
let mut arg_values = arg_values;
|
||||
|
||||
self._call_fn(
|
||||
scope,
|
||||
&mut GlobalRuntimeState::new(self),
|
||||
&mut Caches::new(),
|
||||
ast,
|
||||
eval_ast,
|
||||
rewind_scope,
|
||||
name.as_ref(),
|
||||
this_ptr,
|
||||
arg_values.as_mut(),
|
||||
)
|
||||
}
|
||||
/// _(internals)_ Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// The following options are available:
|
||||
///
|
||||
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
|
||||
/// * whether to rewind the [`Scope`] after the function call
|
||||
/// * a value for binding to the `this` pointer (if any)
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
///
|
||||
/// # WARNING - Unstable API
|
||||
///
|
||||
/// This API is volatile and may change in the future.
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
///
|
||||
/// This function is _extremely_ low level.
|
||||
///
|
||||
/// A [`GlobalRuntimeState`] and [`Caches`] need to be passed into the function, which can be
|
||||
/// created via [`GlobalRuntimeState::new`] and [`Caches::new`].
|
||||
///
|
||||
/// This makes repeatedly calling particular functions more efficient as the functions
|
||||
/// resolution cache is kept intact.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`. This is to avoid
|
||||
@ -207,56 +204,31 @@ impl Engine {
|
||||
///
|
||||
/// Do not use the arguments after this call. If they are needed afterwards, clone them _before_
|
||||
/// calling this function.
|
||||
#[cfg(feature = "internals")]
|
||||
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
|
||||
#[inline(always)]
|
||||
pub fn call_fn_raw_raw(
|
||||
pub(crate) fn _call_fn(
|
||||
&self,
|
||||
options: CallFnOptions,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
ast: &AST,
|
||||
eval_ast: bool,
|
||||
rewind_scope: bool,
|
||||
name: &str,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
arg_values: &mut [Dynamic],
|
||||
) -> RhaiResult {
|
||||
self._call_fn(
|
||||
scope,
|
||||
global,
|
||||
caches,
|
||||
ast,
|
||||
eval_ast,
|
||||
rewind_scope,
|
||||
name,
|
||||
this_ptr,
|
||||
arg_values,
|
||||
)
|
||||
}
|
||||
|
||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
fn _call_fn(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
ast: &AST,
|
||||
eval_ast: bool,
|
||||
rewind_scope: bool,
|
||||
name: &str,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
arg_values: &mut [Dynamic],
|
||||
) -> RhaiResult {
|
||||
let statements = ast.statements();
|
||||
|
||||
let orig_lib_len = global.lib.len();
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let mut orig_tag = None;
|
||||
|
||||
if let Some(value) = options.tag {
|
||||
orig_tag = Some(mem::replace(&mut global.tag, value));
|
||||
}
|
||||
|
||||
global.lib.push(ast.shared_lib().clone());
|
||||
|
||||
let mut no_this_ptr = Dynamic::NULL;
|
||||
let this_ptr = this_ptr.unwrap_or(&mut no_this_ptr);
|
||||
let this_ptr = options.this_ptr.unwrap_or(&mut no_this_ptr);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let orig_embedded_module_resolver = std::mem::replace(
|
||||
@ -264,7 +236,9 @@ impl Engine {
|
||||
ast.resolver().cloned(),
|
||||
);
|
||||
|
||||
let result = if eval_ast && !statements.is_empty() {
|
||||
let rewind_scope = options.rewind_scope;
|
||||
|
||||
let result = if options.eval_ast && !statements.is_empty() {
|
||||
let orig_scope_len = scope.len();
|
||||
let scope = &mut *RestoreOnDrop::lock_if(rewind_scope, scope, move |s| {
|
||||
s.rewind(orig_scope_len);
|
||||
@ -308,6 +282,11 @@ impl Engine {
|
||||
{
|
||||
global.embedded_module_resolver = orig_embedded_module_resolver;
|
||||
}
|
||||
|
||||
if let Some(value) = orig_tag {
|
||||
global.tag = value;
|
||||
}
|
||||
|
||||
global.lib.truncate(orig_lib_len);
|
||||
|
||||
result
|
||||
|
@ -117,10 +117,10 @@ impl Engine {
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`run_ast_with_scope`][Engine::run_ast_with_scope] instead.
|
||||
/// This method is deprecated. Use [`call_fn_with_options`][Engine::call_fn_with_options] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `call_fn_raw` instead")]
|
||||
#[deprecated(since = "1.1.0", note = "use `call_fn_with_options` instead")]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
pub fn call_fn_dynamic(
|
||||
@ -132,8 +132,56 @@ impl Engine {
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> RhaiResult {
|
||||
#[allow(deprecated)]
|
||||
self.call_fn_raw(scope, ast, eval_ast, true, name, this_ptr, arg_values)
|
||||
}
|
||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
///
|
||||
/// The following options are available:
|
||||
///
|
||||
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
|
||||
/// * whether to rewind the [`Scope`] after the function call
|
||||
/// * a value for binding to the `this` pointer (if any)
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`call_fn_with_options`][Engine::call_fn_with_options] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.12.0", note = "use `call_fn_with_options` instead")]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
pub fn call_fn_raw(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
eval_ast: bool,
|
||||
rewind_scope: bool,
|
||||
name: impl AsRef<str>,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> RhaiResult {
|
||||
let mut arg_values = arg_values;
|
||||
|
||||
let options = crate::CallFnOptions {
|
||||
this_ptr,
|
||||
eval_ast,
|
||||
rewind_scope,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self._call_fn(
|
||||
options,
|
||||
scope,
|
||||
&mut crate::eval::GlobalRuntimeState::new(self),
|
||||
&mut crate::eval::Caches::new(),
|
||||
ast,
|
||||
name.as_ref(),
|
||||
arg_values.as_mut(),
|
||||
)
|
||||
}
|
||||
/// Register a custom fallible function with the [`Engine`].
|
||||
///
|
||||
/// # Deprecated
|
||||
|
@ -4,11 +4,14 @@ use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::parser::ParseState;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
|
||||
reify, Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{any::type_name, mem};
|
||||
use std::{
|
||||
any::{type_name, TypeId},
|
||||
mem,
|
||||
};
|
||||
|
||||
impl Engine {
|
||||
/// Evaluate a string as a script, returning the result value or an error.
|
||||
@ -190,6 +193,11 @@ impl Engine {
|
||||
|
||||
let result = self.eval_ast_with_scope_raw(global, caches, scope, ast)?;
|
||||
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return Ok(reify!(result => T));
|
||||
}
|
||||
|
||||
let typ = self.map_type_name(result.type_name());
|
||||
|
||||
result.try_cast::<T>().ok_or_else(|| {
|
||||
|
@ -150,16 +150,15 @@ impl Engine {
|
||||
|
||||
// Guard against too many operations
|
||||
let max = self.max_operations();
|
||||
let num_operations = global.num_operations;
|
||||
|
||||
if max > 0 && num_operations > max {
|
||||
if max > 0 && global.num_operations > max {
|
||||
return Err(ERR::ErrorTooManyOperations(pos).into());
|
||||
}
|
||||
|
||||
// Report progress
|
||||
self.progress
|
||||
.as_ref()
|
||||
.and_then(|p| p(num_operations))
|
||||
.and_then(|p| p(global.num_operations))
|
||||
.map_or(Ok(()), |token| Err(ERR::ErrorTerminated(token, pos).into()))
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,13 @@ impl<T: Variant + Clone> FuncArgs for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Variant + Clone, const N: usize> FuncArgs for [T; N] {
|
||||
#[inline]
|
||||
fn parse<ARGS: Extend<Dynamic>>(self, args: &mut ARGS) {
|
||||
args.extend(IntoIterator::into_iter(self).map(Dynamic::from));
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to implement [`FuncArgs`] for tuples of standard types (each can be converted into a [`Dynamic`]).
|
||||
macro_rules! impl_args {
|
||||
($($p:ident),*) => {
|
||||
|
@ -7,10 +7,10 @@ use crate::plugin::PluginFunction;
|
||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf,
|
||||
StaticVec, VarDefInfo, ERR,
|
||||
calc_fn_hash, reify, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult,
|
||||
RhaiResultOf, StaticVec, VarDefInfo, ERR,
|
||||
};
|
||||
use std::any::type_name;
|
||||
use std::any::{type_name, TypeId};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@ -305,6 +305,11 @@ impl<'a> NativeCallContext<'a> {
|
||||
|
||||
let result = self._call_fn_raw(fn_name, &mut args, false, false, false)?;
|
||||
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return Ok(reify!(result => T));
|
||||
}
|
||||
|
||||
let typ = self.engine().map_type_name(result.type_name());
|
||||
|
||||
result.try_cast().ok_or_else(|| {
|
||||
@ -330,6 +335,11 @@ impl<'a> NativeCallContext<'a> {
|
||||
|
||||
let result = self._call_fn_raw(fn_name, &mut args, true, false, false)?;
|
||||
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return Ok(reify!(result => T));
|
||||
}
|
||||
|
||||
let typ = self.engine().map_type_name(result.type_name());
|
||||
|
||||
result.try_cast().ok_or_else(|| {
|
||||
|
@ -253,6 +253,9 @@ pub use func::Func;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use ast::ScriptFnMetadata;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use api::call_fn::CallFnOptions;
|
||||
|
||||
/// Variable-sized array of [`Dynamic`] values.
|
||||
///
|
||||
/// Not available under `no_index`.
|
||||
|
@ -23,7 +23,7 @@ use rust_decimal::Decimal;
|
||||
#[inline(always)]
|
||||
fn std_add<T>(x: T, y: T) -> Option<T>
|
||||
where
|
||||
T: Debug + Copy + PartialOrd + num_traits::CheckedAdd<Output = T>,
|
||||
T: num_traits::CheckedAdd<Output = T>,
|
||||
{
|
||||
x.checked_add(&y)
|
||||
}
|
||||
@ -31,14 +31,14 @@ where
|
||||
#[allow(dead_code)]
|
||||
fn regular_add<T>(x: T, y: T) -> Option<T>
|
||||
where
|
||||
T: Debug + Copy + PartialOrd + std::ops::Add<Output = T>,
|
||||
T: std::ops::Add<Output = T>,
|
||||
{
|
||||
Some(x + y)
|
||||
}
|
||||
|
||||
// Range iterator with step
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
pub struct StepRange<T: Debug + Copy + PartialOrd> {
|
||||
pub struct StepRange<T> {
|
||||
pub from: T,
|
||||
pub to: T,
|
||||
pub step: T,
|
||||
@ -46,7 +46,7 @@ pub struct StepRange<T: Debug + Copy + PartialOrd> {
|
||||
pub dir: i8,
|
||||
}
|
||||
|
||||
impl<T: Debug + Copy + PartialOrd> Debug for StepRange<T> {
|
||||
impl<T: Debug> Debug for StepRange<T> {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@ -58,7 +58,7 @@ impl<T: Debug + Copy + PartialOrd> Debug for StepRange<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug + Copy + PartialOrd> StepRange<T> {
|
||||
impl<T: Copy + PartialOrd> StepRange<T> {
|
||||
pub fn new(from: T, to: T, step: T, add: fn(T, T) -> Option<T>) -> RhaiResultOf<Self> {
|
||||
let mut dir = 0;
|
||||
|
||||
@ -95,7 +95,7 @@ impl<T: Debug + Copy + PartialOrd> StepRange<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug + Copy + PartialOrd> Iterator for StepRange<T> {
|
||||
impl<T: Copy + PartialOrd> Iterator for StepRange<T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
@ -118,7 +118,7 @@ impl<T: Debug + Copy + PartialOrd> Iterator for StepRange<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug + Copy + PartialOrd> FusedIterator for StepRange<T> {}
|
||||
impl<T: Copy + PartialOrd> FusedIterator for StepRange<T> {}
|
||||
|
||||
// Bit-field iterator with step
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
|
@ -3818,7 +3818,7 @@ impl Engine {
|
||||
&self,
|
||||
input: &mut TokenStream,
|
||||
state: &mut ParseState,
|
||||
process_settings: impl Fn(&mut ParseSettings),
|
||||
process_settings: impl FnOnce(&mut ParseSettings),
|
||||
_optimization_level: OptimizationLevel,
|
||||
) -> ParseResult<AST> {
|
||||
let mut functions = StraightHashMap::default();
|
||||
@ -3882,7 +3882,7 @@ impl Engine {
|
||||
&self,
|
||||
input: &mut TokenStream,
|
||||
state: &mut ParseState,
|
||||
process_settings: impl Fn(&mut ParseSettings),
|
||||
process_settings: impl FnOnce(&mut ParseSettings),
|
||||
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
|
||||
let mut statements = StmtBlockContainer::new_const();
|
||||
let mut functions = StraightHashMap::default();
|
||||
|
@ -1181,42 +1181,105 @@ impl Dynamic {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn try_cast<T: Any>(self) -> Option<T> {
|
||||
pub fn try_cast<T: Any>(mut self) -> Option<T> {
|
||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
if let Union::Shared(..) = self.0 {
|
||||
return self.flatten().try_cast::<T>();
|
||||
self.flatten_in_place();
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return Some(reify!(self => T));
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||
return match self.0 {
|
||||
Union::Unit(..) => Some(reify!(() => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||
return match self.0 {
|
||||
Union::Int(n, ..) => Some(reify!(n => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
|
||||
return match self.0 {
|
||||
Union::Float(v, ..) => Some(reify!(*v => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
if TypeId::of::<T>() == TypeId::of::<rust_decimal::Decimal>() {
|
||||
return match self.0 {
|
||||
Union::Decimal(v, ..) => Some(reify!(*v => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return match self.0 {
|
||||
Union::Bool(b, ..) => Some(reify!(b => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||
return match self.0 {
|
||||
Union::Str(s, ..) => Some(reify!(s => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||
return match self.0 {
|
||||
Union::Str(s, ..) => Some(reify!(s.to_string() => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||
return match self.0 {
|
||||
Union::Char(c, ..) => Some(reify!(c => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
||||
return match self.0 {
|
||||
Union::Array(a, ..) => Some(reify!(*a => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<crate::Blob>() {
|
||||
return match self.0 {
|
||||
Union::Blob(b, ..) => Some(reify!(*b => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<crate::Map>() {
|
||||
return match self.0 {
|
||||
Union::Map(m, ..) => Some(reify!(*m => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||
return match self.0 {
|
||||
Union::FnPtr(f, ..) => Some(reify!(*f => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||
return match self.0 {
|
||||
Union::TimeStamp(t, ..) => Some(reify!(*t => T)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
reify!(self, |v: T| return Some(v));
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Int(v, ..) => reify!(v => Option<T>),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(v, ..) => reify!(*v => Option<T>),
|
||||
#[cfg(feature = "decimal")]
|
||||
Union::Decimal(v, ..) => reify!(*v => Option<T>),
|
||||
Union::Bool(v, ..) => reify!(v => Option<T>),
|
||||
Union::Str(v, ..) => {
|
||||
reify!(v, |v: T| Some(v), || reify!(v.to_string() => Option<T>))
|
||||
}
|
||||
Union::Char(v, ..) => reify!(v => Option<T>),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(v, ..) => reify!(*v => Option<T>),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(v, ..) => reify!(*v => Option<T>),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(v, ..) => reify!(*v => Option<T>),
|
||||
Union::FnPtr(v, ..) => reify!(*v => Option<T>),
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(v, ..) => reify!(*v => Option<T>),
|
||||
Union::Unit(v, ..) => reify!(v => Option<T>),
|
||||
Union::Variant(v, ..) => (*v).as_boxed_any().downcast().ok().map(|x| *x),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(..) => unreachable!("Union::Shared case should be already handled"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Convert the [`Dynamic`] value into a specific type.
|
||||
@ -1245,6 +1308,11 @@ impl Dynamic {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cast<T: Any + Clone>(self) -> T {
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return reify!(self => T);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let self_type_name = if self.is_shared() {
|
||||
// Avoid panics/deadlocks with shared values
|
||||
|
@ -4,13 +4,13 @@ use crate::eval::GlobalRuntimeState;
|
||||
use crate::tokenizer::is_valid_function_name;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult,
|
||||
RhaiResultOf, StaticVec, AST, ERR,
|
||||
reify, Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
|
||||
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
any::type_name,
|
||||
any::{type_name, TypeId},
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt, mem,
|
||||
};
|
||||
@ -160,6 +160,11 @@ impl FnPtr {
|
||||
|
||||
let result = self.call_raw(&ctx, None, arg_values)?;
|
||||
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return Ok(reify!(result => T));
|
||||
}
|
||||
|
||||
let typ = engine.map_type_name(result.type_name());
|
||||
|
||||
result.try_cast().ok_or_else(|| {
|
||||
@ -184,6 +189,11 @@ impl FnPtr {
|
||||
|
||||
let result = self.call_raw(context, None, arg_values)?;
|
||||
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return Ok(reify!(result => T));
|
||||
}
|
||||
|
||||
let typ = context.engine().map_type_name(result.type_name());
|
||||
|
||||
result.try_cast().ok_or_else(|| {
|
||||
|
@ -80,7 +80,7 @@ impl StringsInterner<'_> {
|
||||
pub fn get_with_mapper<S: AsRef<str>>(
|
||||
&mut self,
|
||||
id: &str,
|
||||
mapper: impl Fn(S) -> ImmutableString,
|
||||
mapper: impl FnOnce(S) -> ImmutableString,
|
||||
text: S,
|
||||
) -> ImmutableString {
|
||||
let key = text.as_ref();
|
||||
|
@ -445,7 +445,8 @@ impl Scope<'_> {
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find(|(.., key)| &name == key)
|
||||
.and_then(|(index, ..)| self.values[len - 1 - index].flatten_clone().try_cast())
|
||||
.map(|(index, ..)| self.values[len - 1 - index].flatten_clone())
|
||||
.and_then(Dynamic::try_cast)
|
||||
}
|
||||
/// Check if the named entry in the [`Scope`] is constant.
|
||||
///
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Func, FuncArgs, Scope, AST, INT};
|
||||
use rhai::{CallFnOptions, Dynamic, Engine, EvalAltResult, FnPtr, Func, FuncArgs, Scope, AST, INT};
|
||||
use std::any::TypeId;
|
||||
|
||||
#[test]
|
||||
@ -50,11 +50,10 @@ fn test_call_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert!(!scope.contains("bar"));
|
||||
|
||||
let args = [(2 as INT).into()];
|
||||
let r = engine
|
||||
.call_fn_raw(&mut scope, &ast, false, false, "define_var", None, args)?
|
||||
.as_int()
|
||||
.unwrap();
|
||||
let options = CallFnOptions::new().eval_ast(false).rewind_scope(false);
|
||||
|
||||
let r =
|
||||
engine.call_fn_with_options::<INT>(options, &mut scope, &ast, "define_var", (2 as INT,))?;
|
||||
assert_eq!(r, 42);
|
||||
|
||||
assert_eq!(
|
||||
@ -87,9 +86,13 @@ fn test_call_fn_scope() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
for _ in 0..50 {
|
||||
assert_eq!(
|
||||
engine
|
||||
.call_fn_raw(&mut scope, &ast, true, false, "foo", None, [Dynamic::THREE])?
|
||||
.as_int()?,
|
||||
engine.call_fn_with_options::<INT>(
|
||||
CallFnOptions::new().rewind_scope(false),
|
||||
&mut scope,
|
||||
&ast,
|
||||
"foo",
|
||||
[Dynamic::THREE],
|
||||
)?,
|
||||
168
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user