rhai/eval/
global_state.rs

1//! Global runtime state.
2
3use crate::{expose_under_internals, Dynamic, Engine, ImmutableString};
4use std::fmt;
5#[cfg(feature = "no_std")]
6use std::prelude::v1::*;
7
8/// Collection of globally-defined constants.
9#[cfg(not(feature = "no_module"))]
10#[cfg(not(feature = "no_function"))]
11pub type SharedGlobalConstants =
12    crate::Shared<crate::Locked<std::collections::BTreeMap<ImmutableString, Dynamic>>>;
13
14/// _(internals)_ Global runtime states.
15/// Exported under the `internals` feature only.
16//
17// # Implementation Notes
18//
19// This implementation for imported [modules][crate::Module] splits the module names from the shared
20// modules to improve data locality.
21//
22// Most usage will be looking up a particular key from the list and then getting the module that
23// corresponds to that key.
24#[derive(Clone)]
25pub struct GlobalRuntimeState {
26    /// Names of imported [modules][crate::Module].
27    #[cfg(not(feature = "no_module"))]
28    imports: crate::ThinVec<ImmutableString>,
29    /// Stack of imported [modules][crate::Module].
30    #[cfg(not(feature = "no_module"))]
31    modules: crate::ThinVec<crate::SharedModule>,
32
33    /// The current stack of loaded [modules][crate::Module] containing script-defined functions.
34    #[cfg(not(feature = "no_function"))]
35    pub lib: crate::StaticVec<crate::SharedModule>,
36    /// Source of the current context.
37    ///
38    /// No source if the string is empty.
39    pub source: Option<ImmutableString>,
40    /// Number of operations performed.
41    pub num_operations: u64,
42    /// Number of modules loaded.
43    #[cfg(not(feature = "no_module"))]
44    pub num_modules_loaded: usize,
45    /// The current nesting level of function calls.
46    pub level: usize,
47    /// Level of the current scope.
48    ///
49    /// The global (root) level is zero, a new block (or function call) is one level higher, and so on.
50    pub scope_level: usize,
51    /// Force a [`Scope`][crate::Scope] search by name.
52    ///
53    /// Normally, access to variables are parsed with a relative offset into the
54    /// [`Scope`][crate::Scope] to avoid a lookup.
55    ///
56    /// In some situation, e.g. after running an `eval` statement, or after a custom syntax
57    /// statement, subsequent offsets may become mis-aligned.
58    ///
59    /// When that happens, this flag is turned on.
60    pub always_search_scope: bool,
61    /// Embedded [module][crate::Module] resolver.
62    #[cfg(not(feature = "no_module"))]
63    pub embedded_module_resolver:
64        Option<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
65    /// Cache of globally-defined constants.
66    ///
67    /// Interior mutability is needed because it is shared in order to aid in cloning.
68    #[cfg(not(feature = "no_module"))]
69    #[cfg(not(feature = "no_function"))]
70    pub constants: Option<SharedGlobalConstants>,
71    /// Custom state that can be used by the external host.
72    pub tag: Dynamic,
73    /// Debugging interface.
74    #[cfg(feature = "debugging")]
75    pub(crate) debugger: Option<Box<super::Debugger>>,
76}
77
78impl Engine {
79    /// _(internals)_ Create a new [`GlobalRuntimeState`] based on an [`Engine`].
80    /// Exported under the `internals` feature only.
81    #[expose_under_internals]
82    #[inline(always)]
83    #[must_use]
84    fn new_global_runtime_state(&self) -> GlobalRuntimeState {
85        GlobalRuntimeState {
86            #[cfg(not(feature = "no_module"))]
87            imports: crate::ThinVec::new(),
88            #[cfg(not(feature = "no_module"))]
89            modules: crate::ThinVec::new(),
90            #[cfg(not(feature = "no_function"))]
91            lib: crate::StaticVec::new(),
92            source: None,
93            num_operations: 0,
94            #[cfg(not(feature = "no_module"))]
95            num_modules_loaded: 0,
96            scope_level: 0,
97            level: 0,
98            always_search_scope: false,
99            #[cfg(not(feature = "no_module"))]
100            embedded_module_resolver: None,
101            #[cfg(not(feature = "no_module"))]
102            #[cfg(not(feature = "no_function"))]
103            constants: None,
104
105            tag: self.default_tag().clone(),
106
107            #[cfg(feature = "debugging")]
108            debugger: self.debugger_interface.as_ref().map(|x| {
109                let dbg = crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init);
110                (x.0)(self, dbg).into()
111            }),
112        }
113    }
114}
115
116impl GlobalRuntimeState {
117    /// Get the length of the stack of globally-imported [modules][crate::Module].
118    ///
119    /// Not available under `no_module`.
120    #[cfg(not(feature = "no_module"))]
121    #[inline(always)]
122    #[must_use]
123    pub fn num_imports(&self) -> usize {
124        self.modules.len()
125    }
126    /// Get the globally-imported [module][crate::Module] at a particular index.
127    ///
128    /// Not available under `no_module`.
129    #[cfg(not(feature = "no_module"))]
130    #[inline]
131    #[must_use]
132    pub fn get_shared_import(&self, index: usize) -> Option<crate::SharedModule> {
133        self.modules.get(index).cloned()
134    }
135    /// Get the index of a globally-imported [module][crate::Module] by name.
136    ///
137    /// Not available under `no_module`.
138    #[cfg(not(feature = "no_module"))]
139    #[inline]
140    #[must_use]
141    pub fn find_import(&self, name: &str) -> Option<usize> {
142        self.imports.iter().rposition(|key| key == name)
143    }
144    /// Push an imported [module][crate::Module] onto the stack.
145    ///
146    /// Not available under `no_module`.
147    #[cfg(not(feature = "no_module"))]
148    #[inline]
149    pub fn push_import(
150        &mut self,
151        name: impl Into<ImmutableString>,
152        module: impl Into<crate::SharedModule>,
153    ) {
154        self.imports.push(name.into());
155
156        self.modules.push(module.into());
157    }
158    /// Truncate the stack of globally-imported [modules][crate::Module] to a particular length.
159    ///
160    /// Not available under `no_module`.
161    #[cfg(not(feature = "no_module"))]
162    #[inline(always)]
163    pub fn truncate_imports(&mut self, size: usize) {
164        self.imports.truncate(size);
165        self.modules.truncate(size);
166    }
167    /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
168    ///
169    /// Not available under `no_module`.
170    #[cfg(not(feature = "no_module"))]
171    #[inline]
172    pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
173        self.imports
174            .iter()
175            .rev()
176            .zip(self.modules.iter().rev())
177            .map(|(name, module)| (name.as_str(), &**module))
178    }
179    /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
180    ///
181    /// Not available under `no_module`.
182    #[cfg(not(feature = "no_module"))]
183    #[inline]
184    pub fn iter_imports_raw(
185        &self,
186    ) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
187        self.imports.iter().rev().zip(self.modules.iter().rev())
188    }
189    /// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
190    ///
191    /// Not available under `no_module`.
192    #[cfg(not(feature = "no_module"))]
193    #[inline]
194    pub fn scan_imports_raw(
195        &self,
196    ) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
197        self.imports.iter().zip(self.modules.iter())
198    }
199    /// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
200    /// globally-imported [modules][crate::Module]?
201    ///
202    /// Not available under `no_module`.
203    #[cfg(not(feature = "no_module"))]
204    #[inline]
205    pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
206        self.modules
207            .iter()
208            .any(|m| m.may_contain_dynamic_fn(hash_script))
209    }
210    /// Does the specified function hash key exist in the stack of globally-imported
211    /// [modules][crate::Module]?
212    ///
213    /// Not available under `no_module`.
214    #[cfg(not(feature = "no_module"))]
215    #[allow(dead_code)]
216    #[inline]
217    #[must_use]
218    pub fn contains_qualified_fn(&self, hash: u64) -> bool {
219        self.modules.iter().any(|m| m.contains_qualified_fn(hash))
220    }
221    /// Get the specified function via its hash key from the stack of globally-imported
222    /// [modules][crate::Module].
223    ///
224    /// Not available under `no_module`.
225    #[cfg(not(feature = "no_module"))]
226    #[inline]
227    #[must_use]
228    pub fn get_qualified_fn(
229        &self,
230        hash: u64,
231        global_namespace_only: bool,
232    ) -> Option<(&crate::func::RhaiFunc, Option<&ImmutableString>)> {
233        if global_namespace_only {
234            self.modules
235                .iter()
236                .rev()
237                .filter(|&m| m.contains_indexed_global_functions())
238                .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
239        } else {
240            self.modules
241                .iter()
242                .rev()
243                .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
244        }
245    }
246    /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
247    /// globally-imported [modules][crate::Module]?
248    ///
249    /// Not available under `no_module`.
250    #[cfg(not(feature = "no_module"))]
251    #[allow(dead_code)]
252    #[inline]
253    #[must_use]
254    pub fn contains_iter(&self, id: std::any::TypeId) -> bool {
255        self.modules.iter().any(|m| m.contains_qualified_iter(id))
256    }
257    /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported
258    /// [modules][crate::Module].
259    ///
260    /// Not available under `no_module`.
261    #[cfg(not(feature = "no_module"))]
262    #[inline]
263    #[must_use]
264    pub fn get_iter(&self, id: std::any::TypeId) -> Option<&crate::func::FnIterator> {
265        self.modules
266            .iter()
267            .rev()
268            .find_map(|m| m.get_qualified_iter(id))
269    }
270    /// Get the current source.
271    #[inline(always)]
272    #[must_use]
273    pub fn source(&self) -> Option<&str> {
274        self.source.as_deref()
275    }
276    /// Get the current source.
277    #[inline(always)]
278    #[must_use]
279    #[allow(dead_code)]
280    pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> {
281        self.source.as_ref()
282    }
283
284    /// Return a reference to the debugging interface.
285    ///
286    /// # Panics
287    ///
288    /// Panics if the debugging interface is not set.
289    #[cfg(feature = "debugging")]
290    #[must_use]
291    pub fn debugger(&self) -> &super::Debugger {
292        self.debugger.as_ref().unwrap()
293    }
294    /// Return a mutable reference to the debugging interface.
295    ///
296    /// # Panics
297    ///
298    /// Panics if the debugging interface is not set.
299    #[cfg(feature = "debugging")]
300    #[must_use]
301    pub fn debugger_mut(&mut self) -> &mut super::Debugger {
302        self.debugger.as_deref_mut().unwrap()
303    }
304}
305
306#[cfg(not(feature = "no_module"))]
307impl<K: Into<ImmutableString>, M: Into<crate::SharedModule>> Extend<(K, M)> for GlobalRuntimeState {
308    #[inline]
309    fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
310        for (k, m) in iter {
311            self.imports.push(k.into());
312            self.modules.push(m.into());
313        }
314    }
315}
316
317impl fmt::Debug for GlobalRuntimeState {
318    #[cold]
319    #[inline(never)]
320    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321        let mut f = f.debug_struct("GlobalRuntimeState");
322
323        #[cfg(not(feature = "no_module"))]
324        f.field("imports", &self.scan_imports_raw().collect::<Vec<_>>())
325            .field("num_modules_loaded", &self.num_modules_loaded)
326            .field("embedded_module_resolver", &self.embedded_module_resolver);
327
328        #[cfg(not(feature = "no_function"))]
329        f.field("lib", &self.lib);
330
331        f.field("source", &self.source)
332            .field("num_operations", &self.num_operations)
333            .field("level", &self.level)
334            .field("scope_level", &self.scope_level)
335            .field("always_search_scope", &self.always_search_scope);
336
337        #[cfg(not(feature = "no_module"))]
338        #[cfg(not(feature = "no_function"))]
339        f.field("constants", &self.constants);
340
341        f.field("tag", &self.tag);
342
343        #[cfg(feature = "debugging")]
344        f.field("debugger", &self.debugger);
345
346        f.finish()
347    }
348}