1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use intaglio::bytes::SymbolTable;

use crate::class;
#[cfg(feature = "core-random")]
use crate::extn::core::random::Random;
use crate::interpreter::InterpreterAllocError;
use crate::load_path;
use crate::module;
use crate::sys;

pub mod output;
pub mod parser;
pub mod regexp;

/// Container for interpreter global state.
///
/// A Ruby interpreter requires various pieces of state to execute Ruby code. It
/// needs an object heap, type registry, symbol table, psuedorandom number
/// generator, and more.
///
/// This struct stores all of these components and allows them to be passed
/// around as one bundle. This is useful in FFI contexts because this `State`
/// can be [`Box`]ed and stored in a user data pointer.
#[derive(Debug)]
pub struct State {
    pub parser: Option<parser::State>,
    pub classes: class::Registry,
    pub modules: module::Registry,
    pub load_path_vfs: load_path::Adapter,
    pub regexp: regexp::State,
    pub symbols: SymbolTable,
    pub output: output::Strategy,
    #[cfg(feature = "core-random")]
    pub prng: Random,
}

impl State {
    /// Create a new `State`.
    ///
    /// The state is comprised of several components:
    ///
    /// - [`Class`] and [`Module`] registries.
    /// - `Regexp` [global state][regexp-state].
    /// - [In-memory virtual filesystem].
    /// - [Ruby parser and file context].
    /// - [Intepreter-level PRNG] (requires activating the `core-random`
    ///   feature).
    /// - [IO capturing] strategy.
    ///
    /// # Errors
    ///
    /// If the `core-random` feature is enabled, this function may return an
    /// error if the interpreter-global psuedorandom number generator fails
    /// to initialize using the platform source of randomness.
    ///
    /// [`Class`]: crate::core::ClassRegistry
    /// [`Module`]: crate::core::ModuleRegistry
    /// [regexp-state]: regexp::State
    /// [In-memory virtual filesystem]: load_path
    /// [Ruby parser and file context]: parser::State
    /// [Intepreter-level PRNG]: Random
    /// [IO capturing]: output::Strategy
    pub fn new() -> Result<Self, InterpreterAllocError> {
        Ok(Self {
            parser: None,
            classes: class::Registry::new(),
            modules: module::Registry::new(),
            load_path_vfs: load_path::Adapter::new(),
            regexp: regexp::State::new(),
            symbols: SymbolTable::new(),
            output: output::Strategy::new(),
            #[cfg(feature = "core-random")]
            prng: Random::new().map_err(|_| InterpreterAllocError::new())?,
        })
    }

    /// Create a new [`parser::State`] from a [`sys::mrb_state`].
    #[doc(hidden)]
    pub(crate) fn try_init_parser(&mut self, mrb: &mut sys::mrb_state) {
        if let Some(parser) = parser::State::new(mrb) {
            if let Some(old_parser) = self.parser.replace(parser) {
                old_parser.close(mrb);
            }
        }
    }
}