artichoke_backend/
interpreter.rsuse std::borrow::Cow;
use std::error;
use std::ffi::c_void;
use std::fmt;
use spinoso_exception::Fatal;
use crate::core::{ClassRegistry, Eval, TryConvertMut};
use crate::error::{Error, RubyException};
use crate::extn;
use crate::ffi;
use crate::gc::{MrbGarbageCollection, State as GcState};
use crate::release_metadata::ReleaseMetadata;
use crate::state::State;
use crate::sys;
use crate::Artichoke;
pub fn interpreter() -> Result<Artichoke, Error> {
let release_meta = ReleaseMetadata::new();
interpreter_with_config(release_meta)
}
pub fn interpreter_with_config(config: ReleaseMetadata<'_>) -> Result<Artichoke, Error> {
let state = State::new()?;
let state = Box::new(state);
let alloc_ud = Box::into_raw(state).cast::<c_void>();
let raw = unsafe { sys::mrb_open_allocf(Some(sys::mrb_default_allocf), alloc_ud) };
let mut interp = unsafe { ffi::from_user_data(raw).map_err(|_| InterpreterAllocError::new())? };
match init(&mut interp, config) {
Ok(()) => Ok(interp),
Err(err) => {
interp.close();
Err(err)
}
}
}
fn init(interp: &mut Artichoke, config: ReleaseMetadata<'_>) -> Result<(), Error> {
if let Some(ref mut state) = interp.state {
let mrb = unsafe { interp.mrb.as_mut() };
state.try_init_parser(mrb);
}
let prior_gc_state = interp.disable_gc()?;
extn::init(interp, config)?;
let mut arena = interp.create_arena_savepoint()?;
unsafe {
arena.interp().with_ffi_boundary(|mrb| sys::mrb_init_mrbgems(mrb))?;
}
arena.restore();
interp.create_arena_savepoint()?.interp().eval(b"")?;
if let GcState::Enabled = prior_gc_state {
interp.enable_gc()?;
interp.full_gc()?;
}
Ok(())
}
#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct InterpreterAllocError {
_private: (),
}
impl InterpreterAllocError {
#[must_use]
pub const fn new() -> Self {
Self { _private: () }
}
}
impl fmt::Display for InterpreterAllocError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Failed to allocate Artichoke interpreter")
}
}
impl error::Error for InterpreterAllocError {}
impl RubyException for InterpreterAllocError {
fn message(&self) -> Cow<'_, [u8]> {
Cow::Borrowed(b"Failed to allocate Artichoke Ruby interpreter")
}
fn name(&self) -> Cow<'_, str> {
"fatal".into()
}
fn vm_backtrace(&self, interp: &mut Artichoke) -> Option<Vec<Vec<u8>>> {
let _ = interp;
None
}
fn as_mrb_value(&self, interp: &mut Artichoke) -> Option<sys::mrb_value> {
let message = interp.try_convert_mut(self.message()).ok()?;
let value = interp.new_instance::<Fatal>(&[message]).ok().flatten()?;
Some(value.inner())
}
}
impl From<InterpreterAllocError> for Error {
fn from(exception: InterpreterAllocError) -> Self {
let err: Box<dyn RubyException> = Box::new(exception);
Self::from(err)
}
}
#[cfg(test)]
mod tests {
#[test]
fn open_close() {
let interp = super::interpreter().unwrap();
interp.close();
}
}