rhai/types/
fn_ptr.rs

1//! The `FnPtr` type.
2
3use crate::func::FnCallArgs;
4use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_function_name, Token};
5use crate::types::dynamic::Variant;
6use crate::{
7    expose_under_internals, Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString,
8    NativeCallContext, Position, RhaiError, RhaiResult, RhaiResultOf, Shared, StaticVec, ThinVec,
9    AST, ERR, PERR,
10};
11#[cfg(feature = "no_std")]
12use std::prelude::v1::*;
13use std::{
14    any::type_name,
15    convert::{TryFrom, TryInto},
16    fmt, mem,
17    ops::{Index, IndexMut},
18};
19
20/// Function pointer type.
21#[derive(Clone, Default)]
22pub enum FnPtrType {
23    /// Normal function pointer.
24    #[default]
25    Normal,
26    /// Linked to a script-defined function.
27    #[cfg(not(feature = "no_function"))]
28    Script(Shared<crate::ast::ScriptFuncDef>),
29    /// Embedded native Rust function.
30    #[cfg(not(feature = "sync"))]
31    Native(Shared<dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + 'static>),
32    #[cfg(feature = "sync")]
33    Native(
34        Shared<dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync + 'static>,
35    ),
36}
37
38impl fmt::Display for FnPtrType {
39    #[cold]
40    #[inline(never)]
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            Self::Normal => f.write_str("Fn"),
44            #[cfg(not(feature = "no_function"))]
45            Self::Script(..) => f.write_str("Fn*"),
46            Self::Native(..) => f.write_str("Fn"),
47        }
48    }
49}
50
51/// A general function pointer, which may carry additional (i.e. curried) argument values
52/// to be passed onto a function during a call.
53#[derive(Clone)]
54pub struct FnPtr {
55    /// Name of the function.
56    pub(crate) name: ImmutableString,
57    /// Curried arguments.
58    pub(crate) curry: ThinVec<Dynamic>,
59    /// Encapsulated environment.
60    #[cfg(not(feature = "no_function"))]
61    pub(crate) env: Option<Shared<crate::ast::EncapsulatedEnviron>>,
62    /// Type of function pointer.
63    pub(crate) typ: FnPtrType,
64}
65
66impl fmt::Display for FnPtr {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        write!(f, "Fn({})", self.fn_name())
69    }
70}
71
72impl fmt::Debug for FnPtr {
73    #[cold]
74    #[inline(never)]
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        let name = match self {
77            #[cfg(not(feature = "no_function"))]
78            _ if self.env.is_some() => format!("{}+", self.typ),
79            _ => self.typ.to_string(),
80        };
81        let ff = &mut f.debug_tuple(&name);
82        ff.field(&self.name);
83        self.curry.iter().for_each(|curry| {
84            ff.field(curry);
85        });
86        ff.finish()?;
87
88        Ok(())
89    }
90}
91
92impl FnPtr {
93    /// Create a new function pointer.
94    ///
95    /// # Errors
96    ///
97    /// Returns an error if the function name is not a valid identifier or is a reserved keyword.
98    #[inline(always)]
99    pub fn new(name: impl Into<ImmutableString>) -> RhaiResultOf<Self> {
100        name.into().try_into()
101    }
102    /// Create a new function pointer from a native Rust function.
103    ///
104    /// # Errors
105    ///
106    /// Returns an error if the function name is not a valid identifier or is a reserved keyword.
107    ///
108    /// # WARNING - Unstable API
109    ///
110    /// This API is volatile and may change in the future.
111    ///
112    /// # Callback Function Signature
113    ///
114    /// `Fn(context: NativeCallContext, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>`
115    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
116    #[inline(always)]
117    pub fn from_fn(
118        name: impl Into<ImmutableString>,
119        #[cfg(not(feature = "sync"))] func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult
120            + 'static,
121        #[cfg(feature = "sync")] func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult
122            + Send
123            + Sync
124            + 'static,
125    ) -> RhaiResultOf<Self> {
126        #[allow(deprecated)]
127        Self::from_dyn_fn(name, Box::new(func))
128    }
129    /// Create a new function pointer from a native Rust function.
130    ///
131    /// # Errors
132    ///
133    /// Returns an error if the function name is not a valid identifier or is a reserved keyword.
134    ///
135    /// # WARNING - Unstable API
136    ///
137    /// This API is volatile and may change in the future.
138    ///
139    /// # Callback Function Signature
140    ///
141    /// `Fn(context: NativeCallContext, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>`
142    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
143    #[inline]
144    pub fn from_dyn_fn(
145        name: impl Into<ImmutableString>,
146        #[cfg(not(feature = "sync"))] func: Box<
147            dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + 'static,
148        >,
149        #[cfg(feature = "sync")] func: Box<
150            dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync + 'static,
151        >,
152    ) -> RhaiResultOf<Self> {
153        let mut fp = Self::new(name)?;
154        fp.typ = FnPtrType::Native(Shared::new(func));
155        Ok(fp)
156    }
157
158    /// Get the name of the function.
159    #[inline(always)]
160    #[must_use]
161    pub fn fn_name(&self) -> &str {
162        self.fn_name_raw()
163    }
164    /// Get the name of the function.
165    #[inline(always)]
166    #[must_use]
167    pub(crate) const fn fn_name_raw(&self) -> &ImmutableString {
168        &self.name
169    }
170    /// Get the curried arguments.
171    #[inline(always)]
172    pub fn curry(&self) -> &[Dynamic] {
173        self.curry.as_ref()
174    }
175    /// Iterate the curried arguments.
176    #[inline(always)]
177    pub fn iter_curry(&self) -> impl Iterator<Item = &Dynamic> {
178        self.curry.iter()
179    }
180    /// Mutably-iterate the curried arguments.
181    #[inline(always)]
182    pub fn iter_curry_mut(&mut self) -> impl Iterator<Item = &mut Dynamic> {
183        self.curry.iter_mut()
184    }
185    /// Add a new curried argument.
186    #[inline(always)]
187    pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
188        self.curry.push(value);
189        self
190    }
191    /// Set curried arguments to the function pointer.
192    #[inline]
193    pub fn set_curry(&mut self, values: impl IntoIterator<Item = Dynamic>) -> &mut Self {
194        self.curry = values.into_iter().collect();
195        self
196    }
197    /// Is the function pointer curried?
198    #[inline(always)]
199    #[must_use]
200    pub fn is_curried(&self) -> bool {
201        !self.curry.is_empty()
202    }
203    /// Does the function pointer refer to an anonymous function?
204    ///
205    /// Not available under `no_function`.
206    #[cfg(not(feature = "no_function"))]
207    #[inline(always)]
208    #[must_use]
209    pub fn is_anonymous(&self) -> bool {
210        crate::func::is_anonymous_fn(&self.name)
211    }
212    /// Call the function pointer with curried arguments (if any).
213    /// The function may be script-defined (not available under `no_function`) or native Rust.
214    ///
215    /// This method is intended for calling a function pointer directly, possibly on another [`Engine`].
216    /// Therefore, the [`AST`] is _NOT_ evaluated before calling the function.
217    ///
218    /// # Example
219    ///
220    /// ```
221    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
222    /// # #[cfg(not(feature = "no_function"))]
223    /// # {
224    /// use rhai::{Engine, FnPtr};
225    ///
226    /// let engine = Engine::new();
227    ///
228    /// let ast = engine.compile("fn foo(x, y) { len(x) + y }")?;
229    ///
230    /// let mut fn_ptr = FnPtr::new("foo")?;
231    ///
232    /// // Curry values into the function pointer
233    /// fn_ptr.set_curry(vec!["abc".into()]);
234    ///
235    /// // Values are only needed for non-curried parameters
236    /// let result: i64 = fn_ptr.call(&engine, &ast, ( 39_i64, ) )?;
237    ///
238    /// assert_eq!(result, 42);
239    /// # }
240    /// # Ok(())
241    /// # }
242    /// ```
243    #[inline]
244    pub fn call<T: Variant + Clone>(
245        &self,
246        engine: &Engine,
247        ast: &AST,
248        args: impl FuncArgs,
249    ) -> RhaiResultOf<T> {
250        let _ast = ast;
251        let mut arg_values = StaticVec::new_const();
252        args.parse(&mut arg_values);
253
254        let global = &mut engine.new_global_runtime_state();
255
256        #[cfg(not(feature = "no_function"))]
257        global.lib.push(_ast.shared_lib().clone());
258
259        let ctx = (engine, self.fn_name(), None, &*global, Position::NONE).into();
260
261        self.call_raw(&ctx, None, arg_values).and_then(|result| {
262            result.try_cast_result().map_err(|r| {
263                let result_type = engine.map_type_name(r.type_name());
264                let cast_type = match type_name::<T>() {
265                    typ if typ.contains("::") => engine.map_type_name(typ),
266                    typ => typ,
267                };
268                ERR::ErrorMismatchOutputType(cast_type.into(), result_type.into(), Position::NONE)
269                    .into()
270            })
271        })
272    }
273    /// Call the function pointer with curried arguments (if any).
274    /// The function may be script-defined (not available under `no_function`) or native Rust.
275    ///
276    /// This method is intended for calling a function pointer that is passed into a native Rust
277    /// function as an argument.  Therefore, the [`AST`] is _NOT_ evaluated before calling the
278    /// function.
279    #[inline]
280    pub fn call_within_context<T: Variant + Clone>(
281        &self,
282        context: &NativeCallContext,
283        args: impl FuncArgs,
284    ) -> RhaiResultOf<T> {
285        let mut arg_values = StaticVec::new_const();
286        args.parse(&mut arg_values);
287
288        self.call_raw(context, None, arg_values).and_then(|result| {
289            result.try_cast_result().map_err(|r| {
290                let result_type = context.engine().map_type_name(r.type_name());
291                let cast_type = match type_name::<T>() {
292                    typ if typ.contains("::") => context.engine().map_type_name(typ),
293                    typ => typ,
294                };
295                ERR::ErrorMismatchOutputType(cast_type.into(), result_type.into(), Position::NONE)
296                    .into()
297            })
298        })
299    }
300    /// Call the function pointer with curried arguments (if any).
301    /// The function may be script-defined (not available under `no_function`) or native Rust.
302    ///
303    /// This method is intended for calling a function pointer that is passed into a native Rust
304    /// function as an argument.  Therefore, the [`AST`] is _NOT_ evaluated before calling the
305    /// function.
306    ///
307    /// # WARNING - Low Level API
308    ///
309    /// This function is very low level.
310    ///
311    /// # Arguments
312    ///
313    /// All the arguments are _consumed_, meaning that they're replaced by `()`.
314    /// This is to avoid unnecessarily cloning the arguments.
315    ///
316    /// Do not use the arguments after this call. If they are needed afterwards,
317    /// clone them _before_ calling this function.
318    #[inline]
319    pub fn call_raw(
320        &self,
321        context: &NativeCallContext,
322        this_ptr: Option<&mut Dynamic>,
323        arg_values: impl AsMut<[Dynamic]>,
324    ) -> RhaiResult {
325        let mut arg_values = arg_values;
326        let mut arg_values = arg_values.as_mut();
327        let mut args_data;
328
329        if self.is_curried() {
330            args_data = FnArgsVec::with_capacity(self.curry().len() + arg_values.len());
331            args_data.extend(self.curry().iter().cloned());
332            args_data.extend(arg_values.iter_mut().map(mem::take));
333            arg_values = &mut *args_data;
334        };
335
336        let args = &mut StaticVec::with_capacity(arg_values.len() + 1);
337        args.extend(arg_values.iter_mut());
338
339        match self.typ {
340            // Linked to scripted function?
341            #[cfg(not(feature = "no_function"))]
342            FnPtrType::Script(ref fn_def) if fn_def.params.len() == args.len() => {
343                let global = &mut context.global_runtime_state().clone();
344                global.level += 1;
345
346                let caches = &mut crate::eval::Caches::new();
347
348                return context.engine().call_script_fn(
349                    global,
350                    caches,
351                    &mut crate::Scope::new(),
352                    this_ptr,
353                    #[cfg(not(feature = "no_function"))]
354                    self.env.as_deref(),
355                    #[cfg(feature = "no_function")]
356                    None,
357                    fn_def,
358                    args,
359                    true,
360                    context.call_position(),
361                );
362            }
363            _ => (),
364        }
365
366        let is_method = this_ptr.is_some();
367
368        if let Some(obj) = this_ptr {
369            args.insert(0, obj);
370        }
371
372        context.call_fn_raw(self.fn_name(), is_method, is_method, args)
373    }
374
375    /// _(internals)_ Make a call to a function pointer with either a specified number of arguments,
376    /// or with extra arguments attached.
377    /// Exported under the `internals` feature only.
378    ///
379    /// If `this_ptr` is provided, it is first provided to script-defined functions bound to `this`.
380    ///
381    /// When an appropriate function is not found and `move_this_ptr_to_args` is `Some`, `this_ptr`
382    /// is removed and inserted as the appropriate parameter number.
383    ///
384    /// This is useful for calling predicate closures within an iteration loop where the extra
385    /// argument is the current element's index.
386    ///
387    /// If the function pointer is linked to a scripted function definition, use the appropriate
388    /// number of arguments to call it directly (one version attaches extra arguments).
389    #[expose_under_internals]
390    #[inline(always)]
391    fn call_raw_with_extra_args<const N: usize, const E: usize>(
392        &self,
393        caller_fn: &str,
394        ctx: &NativeCallContext,
395        this_ptr: Option<&mut Dynamic>,
396        args: [Dynamic; N],
397        extras: [Dynamic; E],
398        move_this_ptr_to_args: Option<usize>,
399    ) -> RhaiResult {
400        match move_this_ptr_to_args {
401            Some(m) => {
402                self._call_with_extra_args::<true, N, E>(caller_fn, ctx, this_ptr, args, extras, m)
403            }
404            None => {
405                self._call_with_extra_args::<false, N, E>(caller_fn, ctx, this_ptr, args, extras, 0)
406            }
407        }
408    }
409    /// Make a call to a function pointer with either a specified number of arguments, or with extra
410    /// arguments attached.
411    fn _call_with_extra_args<const MOVE_PTR: bool, const N: usize, const E: usize>(
412        &self,
413        caller_fn: &str,
414        ctx: &NativeCallContext,
415        mut this_ptr: Option<&mut Dynamic>,
416        args: [Dynamic; N],
417        extras: [Dynamic; E],
418        move_this_ptr_to_args: usize,
419    ) -> RhaiResult {
420        match self.typ {
421            #[cfg(not(feature = "no_function"))]
422            FnPtrType::Script(ref fn_def) => {
423                let arity = fn_def.params.len();
424
425                if arity == N + self.curry().len() {
426                    return self.call_raw(ctx, this_ptr, args);
427                }
428                if MOVE_PTR && this_ptr.is_some() {
429                    if arity == N + 1 + self.curry().len() {
430                        let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
431                        if move_this_ptr_to_args == 0 {
432                            args2.push(this_ptr.as_mut().unwrap().clone());
433                            args2.extend(args);
434                        } else {
435                            args2.extend(args);
436                            args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
437                        }
438                        return self.call_raw(ctx, None, args2);
439                    }
440                    if arity == N + E + 1 + self.curry().len() {
441                        let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
442                        if move_this_ptr_to_args == 0 {
443                            args2.push(this_ptr.as_mut().unwrap().clone());
444                            args2.extend(args);
445                        } else {
446                            args2.extend(args);
447                            args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
448                        }
449                        args2.extend(extras);
450                        return self.call_raw(ctx, None, args2);
451                    }
452                }
453                if arity == N + E + self.curry().len() {
454                    let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
455                    args2.extend(args);
456                    args2.extend(extras);
457                    return self.call_raw(ctx, this_ptr, args2);
458                }
459            }
460            _ => (),
461        }
462
463        self.call_raw(ctx, this_ptr.as_deref_mut(), args.clone())
464            .or_else(|err| match *err {
465                ERR::ErrorFunctionNotFound(sig, ..)
466                    if MOVE_PTR && this_ptr.is_some() && sig.starts_with(self.fn_name()) =>
467                {
468                    let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
469                    if move_this_ptr_to_args == 0 {
470                        args2.push(this_ptr.as_mut().unwrap().clone());
471                        args2.extend(args.clone());
472                    } else {
473                        args2.extend(args.clone());
474                        args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
475                    }
476                    self.call_raw(ctx, None, args2)
477                }
478                _ => Err(err),
479            })
480            .or_else(|err| match *err {
481                ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(self.fn_name()) => {
482                    if MOVE_PTR {
483                        if let Some(ref mut this_ptr) = this_ptr {
484                            let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
485                            if move_this_ptr_to_args == 0 {
486                                args2.push(this_ptr.clone());
487                                args2.extend(args);
488                                args2.extend(extras);
489                            } else {
490                                args2.extend(args);
491                                args2.extend(extras);
492                                args2.insert(move_this_ptr_to_args, this_ptr.clone());
493                            }
494                            return self.call_raw(ctx, None, args2);
495                        }
496                    }
497
498                    let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
499                    args2.extend(args);
500                    args2.extend(extras);
501
502                    self.call_raw(ctx, this_ptr, args2)
503                }
504                _ => Err(err),
505            })
506            .map_err(|err| {
507                Box::new(ERR::ErrorInFunctionCall(
508                    caller_fn.to_string(),
509                    ctx.call_source().unwrap_or("").to_string(),
510                    err,
511                    Position::NONE,
512                ))
513            })
514    }
515}
516
517impl TryFrom<ImmutableString> for FnPtr {
518    type Error = RhaiError;
519
520    #[inline(always)]
521    fn try_from(value: ImmutableString) -> RhaiResultOf<Self> {
522        if is_valid_function_name(&value) {
523            Ok(Self {
524                name: value,
525                curry: ThinVec::new(),
526                #[cfg(not(feature = "no_function"))]
527                env: None,
528                typ: FnPtrType::Normal,
529            })
530        } else if is_reserved_keyword_or_symbol(&value).0
531            || Token::lookup_symbol_from_syntax(&value).is_some()
532        {
533            Err(ERR::ErrorParsing(PERR::Reserved(value.to_string()), Position::NONE).into())
534        } else {
535            Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
536        }
537    }
538}
539
540#[cfg(not(feature = "no_function"))]
541impl<T: Into<Shared<crate::ast::ScriptFuncDef>>> From<T> for FnPtr {
542    #[inline(always)]
543    fn from(value: T) -> Self {
544        let fn_def = value.into();
545
546        Self {
547            name: fn_def.name.clone(),
548            curry: ThinVec::new(),
549            #[cfg(not(feature = "no_function"))]
550            env: None,
551            typ: FnPtrType::Script(fn_def),
552        }
553    }
554}
555
556impl Index<usize> for FnPtr {
557    type Output = Dynamic;
558
559    #[inline(always)]
560    fn index(&self, index: usize) -> &Self::Output {
561        self.curry.index(index)
562    }
563}
564
565impl IndexMut<usize> for FnPtr {
566    #[inline(always)]
567    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
568        self.curry.index_mut(index)
569    }
570}
571
572impl Extend<Dynamic> for FnPtr {
573    #[inline(always)]
574    fn extend<T: IntoIterator<Item = Dynamic>>(&mut self, iter: T) {
575        self.curry.extend(iter);
576    }
577}