1use std::borrow::Cow;
6use std::error;
7use std::fmt;
8use std::mem;
9use std::ptr::{self, NonNull};
10
11use spinoso_exception::Fatal;
12
13use crate::Artichoke;
14use crate::core::{ClassRegistry, TryConvertMut};
15use crate::error::{Error, RubyException};
16use crate::state::State;
17use crate::sys;
18
19pub unsafe fn from_user_data(mrb: *mut sys::mrb_state) -> Result<Artichoke, InterpreterExtractError> {
31 let Some(mut mrb) = NonNull::new(mrb) else {
32 emit_fatal_warning!("ffi: Attempted to extract Artichoke from null `mrb_state`");
33 return Err(InterpreterExtractError::new());
34 };
35
36 let ud = mem::replace(unsafe { &mut mrb.as_mut().ud }, ptr::null_mut());
38 let state = if let Some(state) = NonNull::new(ud) {
39 state.cast::<State>()
40 } else {
41 let alloc_ud = mem::replace(unsafe { &mut mrb.as_mut().allocf_ud }, ptr::null_mut());
43 let Some(state) = NonNull::new(alloc_ud) else {
44 emit_fatal_warning!("ffi: Attempted to extract Artichoke from null `mrb_state->ud` pointer");
45 return Err(InterpreterExtractError::new());
46 };
47 state.cast::<State>()
48 };
49
50 let state = unsafe { Box::from_raw(state.as_ptr()) };
53 Ok(Artichoke::new(mrb, state))
54}
55
56#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
58pub struct InterpreterExtractError {
59 _private: (),
60}
61
62impl InterpreterExtractError {
63 #[must_use]
65 pub const fn new() -> Self {
66 Self { _private: () }
67 }
68}
69
70impl fmt::Display for InterpreterExtractError {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 f.write_str("Failed to extract Artichoke Ruby interpreter from mrb_state userdata")
73 }
74}
75
76impl error::Error for InterpreterExtractError {}
77
78impl RubyException for InterpreterExtractError {
79 fn message(&self) -> Cow<'_, [u8]> {
80 Cow::Borrowed(b"Failed to extract Artichoke Ruby interpreter from mrb_state")
81 }
82
83 fn name(&self) -> Cow<'_, str> {
84 "fatal".into()
85 }
86
87 fn vm_backtrace(&self, interp: &mut Artichoke) -> Option<Vec<Vec<u8>>> {
88 let _ = interp;
89 None
90 }
91
92 fn as_mrb_value(&self, interp: &mut Artichoke) -> Option<sys::mrb_value> {
93 let message = interp.try_convert_mut(self.message()).ok()?;
94 let value = interp.new_instance::<Fatal>(&[message]).ok().flatten()?;
95 Some(value.inner())
96 }
97}
98
99impl From<InterpreterExtractError> for Error {
100 fn from(exception: InterpreterExtractError) -> Self {
101 let err: Box<dyn RubyException> = Box::new(exception);
102 Self::from(err)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use std::ptr::{self, NonNull};
109
110 use crate::ffi;
111 use crate::test::prelude::*;
112
113 #[test]
114 fn from_user_data_null_pointer() {
115 let err = unsafe { ffi::from_user_data(ptr::null_mut()) };
116 assert_eq!(err.err(), Some(InterpreterExtractError::new()));
117 }
118
119 #[test]
120 fn from_user_data_null_user_data() {
121 let mut interp = crate::interpreter().unwrap();
122 let mrb = interp.mrb.as_ptr();
123 let err = unsafe {
124 (*mrb).ud = ptr::null_mut();
126 ffi::from_user_data(mrb)
127 };
128 assert_eq!(err.err(), Some(InterpreterExtractError::new()));
129 interp.mrb = NonNull::new(mrb).unwrap();
130 interp.close();
131 }
132
133 #[test]
134 fn from_user_data() {
135 let interp = crate::interpreter().unwrap();
136 let res = unsafe {
137 let mrb = Artichoke::into_raw(interp);
138 ffi::from_user_data(mrb)
139 };
140 assert!(res.is_ok());
141 res.unwrap().close();
142 }
143}