spo_rhai/func/
native.rs

1//! Module defining interfaces to native-Rust functions.
2
3use super::call::FnCallArgs;
4use crate::ast::FnCallHashes;
5use crate::eval::{Caches, GlobalRuntimeState};
6use crate::plugin::PluginFunc;
7use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
8use crate::types::dynamic::Variant;
9use crate::{
10    calc_fn_hash, Dynamic, Engine, EvalContext, FnArgsVec, FuncArgs, Position, RhaiResult,
11    RhaiResultOf, StaticVec, VarDefInfo, ERR,
12};
13use std::any::type_name;
14#[cfg(feature = "no_std")]
15use std::prelude::v1::*;
16
17/// Trait that maps to `Send + Sync` only under the `sync` feature.
18#[cfg(feature = "sync")]
19pub trait SendSync: Send + Sync {}
20/// Trait that maps to `Send + Sync` only under the `sync` feature.
21#[cfg(feature = "sync")]
22impl<T: Send + Sync> SendSync for T {}
23
24/// Trait that maps to `Send + Sync` only under the `sync` feature.
25#[cfg(not(feature = "sync"))]
26pub trait SendSync {}
27/// Trait that maps to `Send + Sync` only under the `sync` feature.
28#[cfg(not(feature = "sync"))]
29impl<T> SendSync for T {}
30
31/// Immutable reference-counted container.
32#[cfg(not(feature = "sync"))]
33pub use std::rc::Rc as Shared;
34/// Immutable reference-counted container.
35#[cfg(feature = "sync")]
36pub use std::sync::Arc as Shared;
37
38/// Synchronized shared object.
39#[cfg(not(feature = "sync"))]
40pub use std::cell::RefCell as Locked;
41
42/// Read-only lock guard for synchronized shared object.
43#[cfg(not(feature = "sync"))]
44pub type LockGuard<'a, T> = std::cell::Ref<'a, T>;
45
46/// Mutable lock guard for synchronized shared object.
47#[cfg(not(feature = "sync"))]
48pub type LockGuardMut<'a, T> = std::cell::RefMut<'a, T>;
49
50/// Synchronized shared object.
51#[cfg(feature = "sync")]
52#[allow(dead_code)]
53pub use std::sync::RwLock as Locked;
54
55/// Read-only lock guard for synchronized shared object.
56#[cfg(feature = "sync")]
57#[allow(dead_code)]
58pub type LockGuard<'a, T> = std::sync::RwLockReadGuard<'a, T>;
59
60/// Mutable lock guard for synchronized shared object.
61#[cfg(feature = "sync")]
62#[allow(dead_code)]
63pub type LockGuardMut<'a, T> = std::sync::RwLockWriteGuard<'a, T>;
64
65/// Context of a native Rust function call.
66#[derive(Debug)]
67pub struct NativeCallContext<'a> {
68    /// The current [`Engine`].
69    engine: &'a Engine,
70    /// Name of function called.
71    fn_name: &'a str,
72    /// Function source, if any.
73    source: Option<&'a str>,
74    /// The current [`GlobalRuntimeState`], if any.
75    global: &'a GlobalRuntimeState,
76    /// [Position] of the function call.
77    pos: Position,
78}
79
80/// _(internals)_ Context of a native Rust function call, intended for persistence.
81/// Exported under the `internals` feature only.
82///
83/// # WARNING - Volatile Type
84///
85/// This type is volatile and may change in the future.
86#[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."]
87#[cfg(feature = "internals")]
88#[derive(Debug, Clone)]
89pub struct NativeCallContextStore {
90    /// Name of function called.
91    pub fn_name: String,
92    /// Function source, if any.
93    pub source: Option<String>,
94    /// The current [`GlobalRuntimeState`], if any.
95    pub global: GlobalRuntimeState,
96    /// [Position] of the function call.
97    pub pos: Position,
98}
99
100#[cfg(feature = "internals")]
101#[allow(deprecated)]
102impl NativeCallContextStore {
103    /// Create a [`NativeCallContext`] from a [`NativeCallContextStore`].
104    ///
105    /// # WARNING - Unstable API
106    ///
107    /// This API is volatile and may change in the future.
108    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
109    #[inline(always)]
110    #[must_use]
111    pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> {
112        NativeCallContext::from_stored_data(engine, self)
113    }
114}
115
116impl<'a>
117    From<(
118        &'a Engine,
119        &'a str,
120        Option<&'a str>,
121        &'a GlobalRuntimeState,
122        Position,
123    )> for NativeCallContext<'a>
124{
125    #[inline(always)]
126    fn from(
127        value: (
128            &'a Engine,
129            &'a str,
130            Option<&'a str>,
131            &'a GlobalRuntimeState,
132            Position,
133        ),
134    ) -> Self {
135        Self {
136            engine: value.0,
137            fn_name: value.1,
138            source: value.2,
139            global: value.3,
140            pos: value.4,
141        }
142    }
143}
144
145impl<'a> NativeCallContext<'a> {
146    /// _(internals)_ Create a new [`NativeCallContext`].
147    /// Exported under the `internals` feature only.
148    ///
149    /// Not available under `no_module`.
150    #[cfg(feature = "internals")]
151    #[cfg(not(feature = "no_module"))]
152    #[inline(always)]
153    #[must_use]
154    pub const fn new_with_all_fields(
155        engine: &'a Engine,
156        fn_name: &'a str,
157        source: Option<&'a str>,
158        global: &'a GlobalRuntimeState,
159        pos: Position,
160    ) -> Self {
161        Self {
162            engine,
163            fn_name,
164            source,
165            global,
166            pos,
167        }
168    }
169
170    /// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextStore`].
171    /// Exported under the `internals` feature only.
172    ///
173    /// # WARNING - Unstable API
174    ///
175    /// This API is volatile and may change in the future.
176    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
177    #[cfg(feature = "internals")]
178    #[inline]
179    #[must_use]
180    #[allow(deprecated)]
181    pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self {
182        Self {
183            engine,
184            fn_name: &context.fn_name,
185            source: context.source.as_deref(),
186            global: &context.global,
187            pos: context.pos,
188        }
189    }
190    /// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextStore`].
191    /// Exported under the `internals` feature only.
192    ///
193    /// # WARNING - Unstable API
194    ///
195    /// This API is volatile and may change in the future.
196    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
197    #[cfg(feature = "internals")]
198    #[inline]
199    #[must_use]
200    #[allow(deprecated)]
201    pub fn store_data(&self) -> NativeCallContextStore {
202        NativeCallContextStore {
203            fn_name: self.fn_name.to_string(),
204            source: self.source.map(ToString::to_string),
205            global: self.global.clone(),
206            pos: self.pos,
207        }
208    }
209
210    /// The current [`Engine`].
211    #[inline(always)]
212    #[must_use]
213    pub const fn engine(&self) -> &Engine {
214        self.engine
215    }
216    /// Name of the function called.
217    #[inline(always)]
218    #[must_use]
219    pub const fn fn_name(&self) -> &str {
220        self.fn_name
221    }
222    /// [Position] of the function call.
223    #[inline(always)]
224    #[must_use]
225    pub const fn position(&self) -> Position {
226        self.pos
227    }
228    /// Current nesting level of function calls.
229    #[inline(always)]
230    #[must_use]
231    pub const fn call_level(&self) -> usize {
232        self.global.level
233    }
234    /// The current source.
235    #[inline(always)]
236    #[must_use]
237    pub const fn source(&self) -> Option<&str> {
238        self.source
239    }
240    /// Custom state kept in a [`Dynamic`].
241    #[inline(always)]
242    #[must_use]
243    pub const fn tag(&self) -> Option<&Dynamic> {
244        Some(&self.global.tag)
245    }
246    /// Get an iterator over the current set of modules imported via `import` statements
247    /// in reverse order.
248    ///
249    /// Not available under `no_module`.
250    #[cfg(not(feature = "no_module"))]
251    #[inline]
252    pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
253        self.global.iter_imports()
254    }
255    /// _(internals)_ The current [`GlobalRuntimeState`], if any.
256    /// Exported under the `internals` feature only.
257    ///
258    /// Not available under `no_module`.
259    #[cfg(feature = "internals")]
260    #[inline(always)]
261    #[must_use]
262    pub const fn global_runtime_state(&self) -> &GlobalRuntimeState {
263        self.global
264    }
265    /// _(internals)_ The current [`GlobalRuntimeState`], if any.
266    #[cfg(not(feature = "internals"))]
267    #[inline(always)]
268    #[must_use]
269    #[allow(dead_code)]
270    pub(crate) const fn global_runtime_state(&self) -> &GlobalRuntimeState {
271        self.global
272    }
273    /// Get an iterator over the namespaces containing definitions of all script-defined functions
274    /// in reverse order (i.e. parent namespaces are iterated after child namespaces).
275    ///
276    /// Not available under `no_function`.
277    #[cfg(not(feature = "no_function"))]
278    #[inline]
279    pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
280        self.global.lib.iter().map(<_>::as_ref)
281    }
282    /// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions.
283    /// Exported under the `internals` feature only.
284    ///
285    /// Not available under `no_function`.
286    #[cfg(not(feature = "no_function"))]
287    #[cfg(feature = "internals")]
288    #[inline(always)]
289    #[must_use]
290    pub fn namespaces(&self) -> &[crate::SharedModule] {
291        &self.global.lib
292    }
293    /// Call a function inside the call context with the provided arguments.
294    #[inline]
295    pub fn call_fn<T: Variant + Clone>(
296        &self,
297        fn_name: impl AsRef<str>,
298        args: impl FuncArgs,
299    ) -> RhaiResultOf<T> {
300        let mut arg_values = StaticVec::new_const();
301        args.parse(&mut arg_values);
302
303        let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
304
305        self._call_fn_raw(fn_name, args, false, false, false)
306            .and_then(|result| {
307                result.try_cast_raw().map_err(|r| {
308                    let result_type = self.engine().map_type_name(r.type_name());
309                    let cast_type = match type_name::<T>() {
310                        typ if typ.contains("::") => self.engine.map_type_name(typ),
311                        typ => typ,
312                    };
313                    ERR::ErrorMismatchOutputType(
314                        cast_type.into(),
315                        result_type.into(),
316                        Position::NONE,
317                    )
318                    .into()
319                })
320            })
321    }
322    /// Call a registered native Rust function inside the call context with the provided arguments.
323    ///
324    /// This is often useful because Rust functions typically only want to cross-call other
325    /// registered Rust functions and not have to worry about scripted functions hijacking the
326    /// process unknowingly (or deliberately).
327    #[inline]
328    pub fn call_native_fn<T: Variant + Clone>(
329        &self,
330        fn_name: impl AsRef<str>,
331        args: impl FuncArgs,
332    ) -> RhaiResultOf<T> {
333        let mut arg_values = StaticVec::new_const();
334        args.parse(&mut arg_values);
335
336        let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
337
338        self._call_fn_raw(fn_name, args, true, false, false)
339            .and_then(|result| {
340                result.try_cast_raw().map_err(|r| {
341                    let result_type = self.engine().map_type_name(r.type_name());
342                    let cast_type = match type_name::<T>() {
343                        typ if typ.contains("::") => self.engine.map_type_name(typ),
344                        typ => typ,
345                    };
346                    ERR::ErrorMismatchOutputType(
347                        cast_type.into(),
348                        result_type.into(),
349                        Position::NONE,
350                    )
351                    .into()
352                })
353            })
354    }
355    /// Call a function (native Rust or scripted) inside the call context.
356    ///
357    /// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for
358    /// a script-defined function (or the object of a method call).
359    ///
360    /// # WARNING - Low Level API
361    ///
362    /// This function is very low level.
363    ///
364    /// # Arguments
365    ///
366    /// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid
367    /// unnecessarily cloning the arguments.
368    ///
369    /// **DO NOT** reuse the arguments after this call. If they are needed afterwards, clone them
370    /// _before_ calling this function.
371    ///
372    /// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
373    /// not consumed.
374    #[inline(always)]
375    pub fn call_fn_raw(
376        &self,
377        fn_name: impl AsRef<str>,
378        is_ref_mut: bool,
379        is_method_call: bool,
380        args: &mut [&mut Dynamic],
381    ) -> RhaiResult {
382        let name = fn_name.as_ref();
383        let native_only = !is_valid_function_name(name);
384        #[cfg(not(feature = "no_function"))]
385        let native_only = native_only && !crate::parser::is_anonymous_fn(name);
386
387        self._call_fn_raw(fn_name, args, native_only, is_ref_mut, is_method_call)
388    }
389    /// Call a registered native Rust function inside the call context.
390    ///
391    /// This is often useful because Rust functions typically only want to cross-call other
392    /// registered Rust functions and not have to worry about scripted functions hijacking the
393    /// process unknowingly (or deliberately).
394    ///
395    /// # WARNING - Low Level API
396    ///
397    /// This function is very low level.
398    ///
399    /// # Arguments
400    ///
401    /// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid
402    /// unnecessarily cloning the arguments.
403    ///
404    /// **DO NOT** reuse the arguments after this call. If they are needed afterwards, clone them
405    /// _before_ calling this function.
406    ///
407    /// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
408    /// not consumed.
409    #[inline(always)]
410    pub fn call_native_fn_raw(
411        &self,
412        fn_name: impl AsRef<str>,
413        is_ref_mut: bool,
414        args: &mut [&mut Dynamic],
415    ) -> RhaiResult {
416        self._call_fn_raw(fn_name, args, true, is_ref_mut, false)
417    }
418
419    /// Call a function (native Rust or scripted) inside the call context.
420    fn _call_fn_raw(
421        &self,
422        fn_name: impl AsRef<str>,
423        args: &mut [&mut Dynamic],
424        native_only: bool,
425        is_ref_mut: bool,
426        is_method_call: bool,
427    ) -> RhaiResult {
428        let global = &mut self.global.clone();
429        global.level += 1;
430
431        let caches = &mut Caches::new();
432
433        let fn_name = fn_name.as_ref();
434        let op_token = Token::lookup_symbol_from_syntax(fn_name);
435        let args_len = args.len();
436
437        if native_only {
438            return self
439                .engine()
440                .exec_native_fn_call(
441                    global,
442                    caches,
443                    fn_name,
444                    op_token.as_ref(),
445                    calc_fn_hash(None, fn_name, args_len),
446                    args,
447                    is_ref_mut,
448                    false,
449                    Position::NONE,
450                )
451                .map(|(r, ..)| r);
452        }
453
454        // Native or script
455
456        let hash = match is_method_call {
457            #[cfg(not(feature = "no_function"))]
458            true => FnCallHashes::from_script_and_native(
459                calc_fn_hash(None, fn_name, args_len - 1),
460                calc_fn_hash(None, fn_name, args_len),
461            ),
462            #[cfg(feature = "no_function")]
463            true => FnCallHashes::from_native_only(calc_fn_hash(None, fn_name, args_len)),
464            _ => FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args_len)),
465        };
466
467        self.engine()
468            .exec_fn_call(
469                global,
470                caches,
471                None,
472                fn_name,
473                op_token.as_ref(),
474                hash,
475                args,
476                is_ref_mut,
477                is_method_call,
478                Position::NONE,
479            )
480            .map(|(r, ..)| r)
481    }
482}
483
484/// Return a mutable reference to the wrapped value of a [`Shared`] resource.
485/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
486#[inline(always)]
487#[must_use]
488#[allow(dead_code)]
489pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
490    Shared::make_mut(value)
491}
492
493/// Return a mutable reference to the wrapped value of a [`Shared`] resource.
494#[inline(always)]
495#[must_use]
496#[allow(dead_code)]
497pub fn shared_get_mut<T: Clone>(value: &mut Shared<T>) -> Option<&mut T> {
498    Shared::get_mut(value)
499}
500
501/// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise.
502#[inline]
503#[must_use]
504#[allow(dead_code)]
505pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
506    shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone())
507}
508
509/// Consume a [`Shared`] resource if is unique (i.e. not shared).
510#[inline(always)]
511#[allow(dead_code)]
512pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
513    Shared::try_unwrap(value)
514}
515
516/// Consume a [`Shared`] resource, assuming that it is unique (i.e. not shared).
517///
518/// # Panics
519///
520/// Panics if the resource is shared (i.e. has other outstanding references).
521#[inline]
522#[must_use]
523#[allow(dead_code)]
524pub fn shared_take<T>(value: Shared<T>) -> T {
525    shared_try_take(value)
526        .ok()
527        .unwrap_or_else(|| panic!("`value` is shared (i.e. has outstanding references)"))
528}
529
530/// _(internals)_ Lock a [`Locked`] resource for immutable access.
531/// Exported under the `internals` feature only.
532#[inline(always)]
533#[must_use]
534#[allow(dead_code)]
535pub fn locked_read<T>(value: &Locked<T>) -> LockGuard<T> {
536    #[cfg(not(feature = "sync"))]
537    return value.borrow();
538
539    #[cfg(feature = "sync")]
540    return value.read().unwrap();
541}
542
543/// _(internals)_ Lock a [`Locked`] resource for mutable access.
544/// Exported under the `internals` feature only.
545#[inline(always)]
546#[must_use]
547#[allow(dead_code)]
548pub fn locked_write<T>(value: &Locked<T>) -> LockGuardMut<T> {
549    #[cfg(not(feature = "sync"))]
550    return value.borrow_mut();
551
552    #[cfg(feature = "sync")]
553    return value.write().unwrap();
554}
555
556/// General Rust function trail object.
557#[cfg(not(feature = "sync"))]
558pub type FnAny = dyn Fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult;
559/// General Rust function trail object.
560#[cfg(feature = "sync")]
561pub type FnAny = dyn Fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult + Send + Sync;
562
563/// Built-in function trait object.
564pub type FnBuiltin = (
565    fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult,
566    bool,
567);
568
569/// Function that gets an iterator from a type.
570#[cfg(not(feature = "sync"))]
571pub type FnIterator = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>>;
572/// Function that gets an iterator from a type.
573#[cfg(feature = "sync")]
574pub type FnIterator =
575    dyn Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>> + Send + Sync;
576
577/// Plugin function trait object.
578#[cfg(not(feature = "sync"))]
579pub type FnPlugin = dyn PluginFunc;
580/// Plugin function trait object.
581#[cfg(feature = "sync")]
582pub type FnPlugin = dyn PluginFunc + Send + Sync;
583
584/// Callback function for progress reporting.
585#[cfg(not(feature = "unchecked"))]
586#[cfg(not(feature = "sync"))]
587pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic>;
588/// Callback function for progress reporting.
589#[cfg(not(feature = "unchecked"))]
590#[cfg(feature = "sync")]
591pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic> + Send + Sync;
592
593/// Callback function for printing.
594#[cfg(not(feature = "sync"))]
595pub type OnPrintCallback = dyn Fn(&str);
596/// Callback function for printing.
597#[cfg(feature = "sync")]
598pub type OnPrintCallback = dyn Fn(&str) + Send + Sync;
599
600/// Callback function for debugging.
601#[cfg(not(feature = "sync"))]
602pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position);
603/// Callback function for debugging.
604#[cfg(feature = "sync")]
605pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position) + Send + Sync;
606
607/// Callback function for mapping tokens during parsing.
608#[cfg(not(feature = "sync"))]
609pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token;
610/// Callback function for mapping tokens during parsing.
611#[cfg(feature = "sync")]
612pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token + Send + Sync;
613
614/// Callback function for variable access.
615#[cfg(not(feature = "sync"))]
616pub type OnVarCallback = dyn Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>>;
617/// Callback function for variable access.
618#[cfg(feature = "sync")]
619pub type OnVarCallback =
620    dyn Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync;
621
622/// Callback function for variable definition.
623#[cfg(not(feature = "sync"))]
624pub type OnDefVarCallback = dyn Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool>;
625/// Callback function for variable definition.
626#[cfg(feature = "sync")]
627pub type OnDefVarCallback =
628    dyn Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool> + Send + Sync;