artichoke_backend/state/
mod.rs

1use core::hash::BuildHasherDefault;
2use std::collections::hash_map::RandomState;
3
4use intaglio::bytes::SymbolTable;
5use mezzaluna_conversion_methods::ConvMethods;
6use mezzaluna_type_registry::Registry;
7use rustc_hash::FxHasher;
8
9use crate::class;
10#[cfg(feature = "core-random")]
11use crate::extn::core::random::Random;
12use crate::interpreter::InterpreterAllocError;
13use crate::load_path;
14use crate::module;
15use crate::sys;
16
17pub mod output;
18pub mod parser;
19
20type SymbolTableHasher = BuildHasherDefault<FxHasher>;
21
22/// Container for interpreter global state.
23///
24/// A Ruby interpreter requires various pieces of state to execute Ruby code. It
25/// needs an object heap, type registry, symbol table, pseudorandom number
26/// generator, and more.
27///
28/// This struct stores all of these components and allows them to be passed
29/// around as one bundle. This is useful in FFI contexts because this `State`
30/// can be [`Box`]ed and stored in a user data pointer.
31#[derive(Debug)]
32pub struct State {
33    pub parser: Option<parser::State>,
34    pub classes: Registry<class::Spec>,
35    pub modules: Registry<module::Spec>,
36    pub load_path_vfs: load_path::Adapter,
37    #[cfg(feature = "core-regexp")]
38    pub regexp: spinoso_regexp::State,
39    pub symbols: SymbolTable<SymbolTableHasher>,
40    pub output: output::Strategy,
41    pub hash_builder: RandomState,
42    #[cfg(feature = "core-random")]
43    pub prng: Random,
44    pub conv_method_table: ConvMethods,
45}
46
47impl State {
48    /// Create a new `State`.
49    ///
50    /// The state is comprised of several components:
51    ///
52    /// - [`Class`] and [`Module`] registries.
53    #[cfg_attr(feature = "core-regexp", doc = "- `Regexp` [global state][regexp-state].")]
54    /// - [In-memory virtual file system].
55    /// - [Ruby parser and file context].
56    #[cfg_attr(feature = "core-random", doc = "- [Intepreter-level PRNG].")]
57    /// - [IO capturing] strategy.
58    ///
59    /// # Errors
60    ///
61    /// If the `core-random` feature is enabled, this function may return an
62    /// error if the interpreter-global pseudorandom number generator fails
63    /// to initialize using the platform source of randomness.
64    ///
65    /// [`Class`]: crate::core::ClassRegistry
66    /// [`Module`]: crate::core::ModuleRegistry
67    #[cfg_attr(feature = "core-regexp", doc = "[regexp-state]: spinoso_regexp::State")]
68    /// [In-memory virtual file system]: load_path
69    /// [Ruby parser and file context]: parser::State
70    #[cfg_attr(feature = "core-random", doc = "[Intepreter-level PRNG]: Random")]
71    /// [IO capturing]: output::Strategy
72    pub fn new() -> Result<Self, InterpreterAllocError> {
73        Ok(Self {
74            parser: None,
75            classes: Registry::new(),
76            modules: Registry::new(),
77            load_path_vfs: load_path::Adapter::new(),
78            #[cfg(feature = "core-regexp")]
79            regexp: spinoso_regexp::State::new(),
80            symbols: SymbolTable::with_hasher(SymbolTableHasher::default()),
81            output: output::Strategy::new(),
82            hash_builder: RandomState::new(),
83            #[cfg(feature = "core-random")]
84            prng: Random::new().map_err(|_| InterpreterAllocError::new())?,
85            conv_method_table: ConvMethods::new(),
86        })
87    }
88
89    /// Create a new [`parser::State`] from a [`sys::mrb_state`].
90    #[doc(hidden)]
91    pub(crate) fn try_init_parser(&mut self, mrb: &mut sys::mrb_state) {
92        if let Some(parser) = parser::State::new(mrb) {
93            if let Some(old_parser) = self.parser.replace(parser) {
94                old_parser.close(mrb);
95            }
96        }
97    }
98}