spo_rhai/types/
fn_ptr.rs

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