//! Module that defines the `call_fn` API of [`Engine`]. #![cfg(not(feature = "no_function"))] use crate::eval::{Caches, GlobalRuntimeState}; use crate::types::dynamic::Variant; 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::*; impl Engine { /// Call a script function defined in an [`AST`] with multiple arguments. /// /// Not available under `no_function`. /// /// 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`]. /// /// # Example /// /// ``` /// # fn main() -> Result<(), Box> { /// # #[cfg(not(feature = "no_function"))] /// # { /// use rhai::{Engine, Scope}; /// /// 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 } /// ")?; /// /// let mut scope = Scope::new(); /// scope.push("foo", 42_i64); /// /// // Call the script-defined function /// let result = engine.call_fn::(&mut scope, &ast, "add", ( "abc", 123_i64 ) )?; /// assert_eq!(result, 168); /// /// let result = engine.call_fn::(&mut scope, &ast, "add1", ( "abc", ) )?; /// // ^^^^^^^^^^ tuple of one /// assert_eq!(result, 46); /// /// let result = engine.call_fn::(&mut scope, &ast, "bar", () )?; /// assert_eq!(result, 21); /// # } /// # Ok(()) /// # } /// ``` #[inline] pub fn call_fn( &self, scope: &mut Scope, ast: &AST, name: impl AsRef, args: impl FuncArgs, ) -> RhaiResultOf { 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)?; // Bail out early if the return type needs no cast if TypeId::of::() == TypeId::of::() { return Ok(reify!(result => T)); } if TypeId::of::() == TypeId::of::<()>() { return Ok(reify!(() => T)); } // Cast return type let typ = self.map_type_name(result.type_name()); result.try_cast().ok_or_else(|| { let t = self.map_type_name(type_name::()).into(); ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into() }) } /// 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> { /// # #[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::(), 168); /// /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "add1", None, [ "abc".into() ])?; /// assert_eq!(result.cast::(), 46); /// /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "bar", None, [])?; /// assert_eq!(result.cast::(), 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::("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, 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 unnecessarily cloning the arguments. /// /// 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( &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 { 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_scope_len = scope.len(); #[cfg(not(feature = "no_module"))] let orig_embedded_module_resolver = std::mem::replace( &mut global.embedded_module_resolver, ast.resolver().cloned(), ); if eval_ast && !statements.is_empty() { self.eval_global_statements(scope, global, caches, statements, &[ast.as_ref()], 0)?; if rewind_scope { scope.rewind(orig_scope_len); } } let mut this_ptr = this_ptr; let mut args: StaticVec<_> = arg_values.iter_mut().collect(); // Check for data race. #[cfg(not(feature = "no_closure"))] crate::func::call::ensure_no_data_race(name, &args, false)?; let lib = &[ast.as_ref()]; let fn_def = ast .shared_lib() .get_script_fn(name, args.len()) .ok_or_else(|| ERR::ErrorFunctionNotFound(name.into(), Position::NONE))?; let result = self.call_script_fn( scope, global, caches, lib, &mut this_ptr, fn_def, &mut args, rewind_scope, Position::NONE, 0, )?; #[cfg(not(feature = "no_module"))] { global.embedded_module_resolver = orig_embedded_module_resolver; } #[cfg(feature = "debugging")] if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let node = &crate::ast::Stmt::Noop(Position::NONE); self.run_debugger(scope, global, lib, &mut this_ptr, node, 0)?; } Ok(result) } }