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
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::mem;
use std::rc::Rc;

use crate::class;
use crate::def::{EnclosingRubyScope, Free};
use crate::eval::EvalContext;
use crate::fs::MrbFilesystem;
use crate::module;
use crate::sys::{self, DescribeState};

// NOTE: MrbState assumes that it it is stored in `mrb_state->ud` wrapped in a
// [`Rc`] with type [`Mrb`] as created by [`crate::interpreter`].
pub struct State {
    pub mrb: *mut sys::mrb_state,
    pub ctx: *mut sys::mrbc_context,
    classes: HashMap<TypeId, Rc<RefCell<class::Spec>>>,
    modules: HashMap<TypeId, Rc<RefCell<module::Spec>>>,
    pub vfs: MrbFilesystem,
    pub(crate) context_stack: Vec<EvalContext>,
    pub num_set_regexp_capture_globals: usize,
    symbol_cache: HashMap<String, sys::mrb_sym>,
}

impl State {
    /// Create a new [`State`] from a [`sys::mrb_state`] and
    /// [`sys::mrbc_context`] with an
    /// [in memory virtual filesystem](MrbFilesystem).
    pub fn new(mrb: *mut sys::mrb_state, ctx: *mut sys::mrbc_context, vfs: MrbFilesystem) -> Self {
        Self {
            mrb,
            ctx,
            classes: HashMap::default(),
            modules: HashMap::default(),
            vfs,
            context_stack: vec![],
            num_set_regexp_capture_globals: 0,
            symbol_cache: HashMap::default(),
        }
    }

    /// Close a [`State`] and free underlying mruby structs and memory.
    pub fn close(self) {
        drop(self)
    }

    /// Create a class definition bound to a Rust type `T`. Class definitions
    /// have the same lifetime as the [`State`] because the class def owns the
    /// `mrb_data_type` for the type, which must be long-lived. Class defs are
    /// stored by [`TypeId`] of `T`.
    ///
    /// Internally, [`class::Spec`]s are stored in an `Rc<RefCell<_>>` which
    /// allows class specs to have multiple owners, such as being a super class
    /// or an enclosing scope for a class or a module. To mutate the class spec,
    /// call `borrow_mut` on the return value of this method to get a mutable
    /// reference to the class spec.
    ///
    /// Class specs can also be retrieved from the state after creation with
    /// [`State::class_spec`].
    ///
    /// The recommended pattern for using `def_class` looks like this:
    ///
    /// ```rust
    /// #[macro_use]
    /// extern crate mruby;
    ///
    /// use mruby::convert::FromMrb;
    /// use mruby::def::{ClassLike, Define};
    /// use mruby::sys;
    /// use mruby::value::Value;
    ///
    /// extern "C" fn value(mrb: *mut sys::mrb_state, _slf: sys::mrb_value) -> sys::mrb_value
    /// {
    ///     let interp = unsafe { unwrap_interpreter!(mrb) };
    ///     Value::from_mrb(&interp, 29).inner()
    /// }
    ///
    /// fn main() {
    ///     let interp = mruby::interpreter().expect("mrb init");
    ///     let spec = {
    ///         let mut api = interp.borrow_mut();
    ///         let spec = api.def_class::<()>("Container", None, None);
    ///         spec.borrow_mut().add_method("value", value, sys::mrb_args_none());
    ///         spec.borrow_mut().add_self_method("value", value, sys::mrb_args_none());
    ///         spec.borrow_mut().mrb_value_is_rust_backed(true);
    ///         spec
    ///     };
    ///     spec.borrow().define(&interp).expect("class install");
    /// }
    /// ```
    pub fn def_class<T: Any>(
        &mut self,
        name: &str,
        enclosing_scope: Option<EnclosingRubyScope>,
        free: Option<Free>,
    ) -> Rc<RefCell<class::Spec>> {
        let spec = class::Spec::new(name, enclosing_scope, free);
        let spec = Rc::new(RefCell::new(spec));
        self.classes.insert(TypeId::of::<T>(), Rc::clone(&spec));
        spec
    }

    /// Retrieve a class definition from the state bound to Rust type `T`.
    ///
    /// This function returns `None` if type `T` has not had a class spec
    /// registered for it using [`State::def_class`].
    ///
    /// Internally, [`class::Spec`]s are stored in an `Rc<RefCell<_>>` which
    /// allows class specs to have multiple owners, such as being a super class
    /// or an enclosing scope for a class or a module. To mutate the class spec,
    /// call `borrow_mut` on the return value of this method to get a mutable
    /// reference to the class spec.
    pub fn class_spec<T: Any>(&self) -> Option<Rc<RefCell<class::Spec>>> {
        self.classes.get(&TypeId::of::<T>()).map(Rc::clone)
    }

    /// Create a module definition bound to a Rust type `T`. Module definitions
    /// have the same lifetime as the [`State`]. Module defs are stored by
    /// [`TypeId`] of `T`.
    ///
    /// Internally, [`module::Spec`]s are stored in an `Rc<RefCell<_>>` which
    /// allows module specs to have multiple owners, such as being an enclosing
    /// scope for a class or a module. To mutate the module spec, call
    /// `borrow_mut` on the return value of this method to get a mutable
    /// reference to the module spec.
    ///
    /// Module specs can also be retrieved from the state after creation with
    /// [`State::module_spec`].
    ///
    /// The recommended pattern for using `def_module` looks like this:
    ///
    /// ```rust
    /// #[macro_use]
    /// extern crate mruby;
    ///
    /// use mruby::convert::FromMrb;
    /// use mruby::def::{ClassLike, Define};
    /// use mruby::sys;
    /// use mruby::value::Value;
    ///
    /// extern "C" fn value(mrb: *mut sys::mrb_state, _slf: sys::mrb_value) -> sys::mrb_value
    /// {
    ///     let interp = unsafe { unwrap_interpreter!(mrb) };
    ///     Value::from_mrb(&interp, 29).inner()
    /// }
    ///
    /// fn main() {
    ///     let interp = mruby::interpreter().expect("mrb init");
    ///     let spec = {
    ///         let mut api = interp.borrow_mut();
    ///         let spec = api.def_module::<()>("Container", None);
    ///         spec.borrow_mut().add_method("value", value, sys::mrb_args_none());
    ///         spec.borrow_mut().add_self_method("value", value, sys::mrb_args_none());
    ///         spec
    ///     };
    ///     spec.borrow().define(&interp).expect("class install");
    /// }
    /// ```
    pub fn def_module<T: Any>(
        &mut self,
        name: &str,
        enclosing_scope: Option<EnclosingRubyScope>,
    ) -> Rc<RefCell<module::Spec>> {
        let spec = module::Spec::new(name, enclosing_scope);
        let spec = Rc::new(RefCell::new(spec));
        self.modules.insert(TypeId::of::<T>(), Rc::clone(&spec));
        spec
    }

    /// Retrieve a module definition from the state bound to Rust type `T`.
    ///
    /// This function returns `None` if type `T` has not had a class spec
    /// registered for it using [`State::def_module`].
    ///
    /// Internally, [`module::Spec`]s are stored in an `Rc<RefCell<_>>` which
    /// allows module specs to have multiple owners, such as being an enclosing
    /// scope for a class or a module. To mutate the module spec, call
    /// `borrow_mut` on the return value of this method to get a mutable
    /// reference to the module spec.
    pub fn module_spec<T: Any>(&self) -> Option<Rc<RefCell<module::Spec>>> {
        self.modules.get(&TypeId::of::<T>()).map(Rc::clone)
    }

    pub fn sym_intern(&mut self, sym: &str) -> sys::mrb_sym {
        let mrb = self.mrb;
        let interned = self
            .symbol_cache
            .entry(sym.to_owned())
            .or_insert_with(|| unsafe {
                sys::mrb_intern(mrb, sym.as_ptr() as *const i8, sym.len())
            });
        *interned
    }
}

impl Drop for State {
    fn drop(&mut self) {
        unsafe {
            // At this point, the only ref to the smart poitner wrapping the
            // state is stored in the `mrb_state->ud` pointer. Rematerialize the
            // `Rc`, set the userdata pointer to null, and drop the `Rc` to
            // ensure no memory leaks. After this operation, `Rc::strong_count`
            // will be 0 and the `Rc`, `RefCell`, and `State` will be
            // deallocated.
            let ptr = (*self.mrb).ud;
            if !ptr.is_null() {
                let ud = Rc::from_raw(ptr as *const RefCell<Self>);
                // cleanup pointers
                (*self.mrb).ud = std::ptr::null_mut();
                mem::drop(ud);
            }

            // Free mrb data structures
            sys::mrbc_context_free(self.mrb, self.ctx);
            sys::mrb_close(self.mrb);
            // Cleanup dangling pointers
            self.ctx = std::ptr::null_mut();
            self.mrb = std::ptr::null_mut();
        };
    }
}

impl fmt::Debug for State {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.mrb.debug())
    }
}

impl fmt::Display for State {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.mrb.info())
    }
}