1use std::ffi::{c_char, c_void};
2use std::mem;
3use std::ptr::{self, NonNull};
4
5use crate::sys;
6
7pub unsafe fn funcall(
8 mrb: *mut sys::mrb_state,
9 slf: sys::mrb_value,
10 func: sys::mrb_sym,
11 args: &[sys::mrb_value],
12 block: Option<sys::mrb_value>,
13) -> Result<sys::mrb_value, sys::mrb_value> {
14 let data = Funcall { slf, func, args, block };
15 unsafe { protect(mrb, data) }
17}
18
19pub unsafe fn eval(
20 mrb: *mut sys::mrb_state,
21 context: *mut sys::mrbc_context,
22 code: &[u8],
23) -> Result<sys::mrb_value, sys::mrb_value> {
24 let data = Eval { context, code };
25 unsafe { protect(mrb, data) }
27}
28
29pub unsafe fn block_yield(
30 mrb: *mut sys::mrb_state,
31 block: sys::mrb_value,
32 arg: sys::mrb_value,
33) -> Result<sys::mrb_value, sys::mrb_value> {
34 let data = BlockYield { block, arg };
35 unsafe { protect(mrb, data) }
37}
38
39unsafe fn protect<T>(mrb: *mut sys::mrb_state, data: T) -> Result<sys::mrb_value, sys::mrb_value>
45where
46 T: Protect,
47{
48 let data = unsafe {
52 let data = Box::new(data);
53 let data = Box::into_raw(data);
54 sys::mrb_sys_cptr_value(mrb, data.cast::<c_void>())
55 };
56 let mut state = false;
57
58 let value = unsafe { sys::mrb_protect(mrb, Some(T::run), data, &mut state) };
62
63 unsafe {
67 if let Some(exc) = NonNull::new((*mrb).exc) {
68 (*mrb).exc = ptr::null_mut();
69 Err(sys::mrb_sys_obj_value(exc.cast::<c_void>().as_ptr()))
70 } else if state {
71 Err(value)
72 } else {
73 Ok(value)
74 }
75 }
76}
77
78trait Protect {
79 unsafe extern "C-unwind" fn run(mrb: *mut sys::mrb_state, data: sys::mrb_value) -> sys::mrb_value;
80}
81
82#[derive(Clone, Copy)]
85struct Funcall<'a> {
86 slf: sys::mrb_value,
87 func: u32,
88 args: &'a [sys::mrb_value],
89 block: Option<sys::mrb_value>,
90}
91
92impl Protect for Funcall<'_> {
93 unsafe extern "C-unwind" fn run(mrb: *mut sys::mrb_state, data: sys::mrb_value) -> sys::mrb_value {
94 let Self { slf, func, args, block } = unsafe {
97 let ptr = sys::mrb_sys_cptr_ptr(data);
98 *Box::from_raw(ptr.cast::<Self>())
99 };
100
101 let argslen = if let Ok(argslen) = i64::try_from(args.len()) {
105 argslen
106 } else {
107 return sys::mrb_sys_nil_value();
108 };
109
110 unsafe {
114 if let Some(block) = block {
115 sys::mrb_funcall_with_block(mrb, slf, func, argslen, args.as_ptr(), block)
116 } else {
117 sys::mrb_funcall_argv(mrb, slf, func, argslen, args.as_ptr())
118 }
119 }
120 }
121}
122
123#[derive(Clone, Copy)]
126struct Eval<'a> {
127 context: *mut sys::mrbc_context,
128 code: &'a [u8],
129}
130
131impl Protect for Eval<'_> {
132 unsafe extern "C-unwind" fn run(mrb: *mut sys::mrb_state, data: sys::mrb_value) -> sys::mrb_value {
133 let Self { context, code } = unsafe {
136 let ptr = sys::mrb_sys_cptr_ptr(data);
137 *Box::from_raw(ptr.cast::<Self>())
138 };
139
140 unsafe { sys::mrb_load_nstring_cxt(mrb, code.as_ptr().cast::<c_char>(), code.len(), context) }
150 }
151}
152
153#[derive(Clone, Copy)]
156struct BlockYield {
157 block: sys::mrb_value,
158 arg: sys::mrb_value,
159}
160
161impl Protect for BlockYield {
162 unsafe extern "C-unwind" fn run(mrb: *mut sys::mrb_state, data: sys::mrb_value) -> sys::mrb_value {
163 let Self { block, arg } = unsafe {
166 let ptr = sys::mrb_sys_cptr_ptr(data);
167 *Box::from_raw(ptr.cast::<Self>())
168 };
169 unsafe { sys::mrb_yield(mrb, block, arg) }
172 }
173}
174
175pub unsafe fn is_range(
176 mrb: *mut sys::mrb_state,
177 value: sys::mrb_value,
178 len: i64,
179) -> Result<Option<Range>, sys::mrb_value> {
180 let data = IsRange { value, len };
181 let is_range = unsafe { protect(mrb, data)? };
183 if sys::mrb_sys_value_is_nil(is_range) {
184 Ok(None)
185 } else {
186 let out = unsafe {
189 let ptr = sys::mrb_sys_cptr_ptr(is_range);
190 *Box::from_raw(ptr.cast::<Range>())
191 };
192 Ok(Some(out))
193 }
194}
195
196#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
197pub enum Range {
198 Valid { start: sys::mrb_int, len: sys::mrb_int },
199 Out,
200}
201
202#[derive(Default, Debug, Clone, Copy)]
205struct IsRange {
206 value: sys::mrb_value,
207 len: sys::mrb_int,
208}
209
210impl Protect for IsRange {
211 unsafe extern "C-unwind" fn run(mrb: *mut sys::mrb_state, data: sys::mrb_value) -> sys::mrb_value {
212 use sys::mrb_range_beg_len::{MRB_RANGE_OK, MRB_RANGE_OUT, MRB_RANGE_TYPE_MISMATCH};
213
214 let Self { value, len } = unsafe {
217 let ptr = sys::mrb_sys_cptr_ptr(data);
218 *Box::from_raw(ptr.cast::<Self>())
219 };
220 let mut start = mem::MaybeUninit::<sys::mrb_int>::uninit();
221 let mut range_len = mem::MaybeUninit::<sys::mrb_int>::uninit();
222 let check_range =
225 unsafe { sys::mrb_range_beg_len(mrb, value, start.as_mut_ptr(), range_len.as_mut_ptr(), len, false) };
226 match check_range {
227 MRB_RANGE_OK => {
228 let (start, range_len) = unsafe { (start.assume_init(), range_len.assume_init()) };
231 let out = Some(Range::Valid { start, len: range_len });
232 let out = Box::new(out);
233 let out = Box::into_raw(out);
234 unsafe { sys::mrb_sys_cptr_value(mrb, out.cast::<c_void>()) }
236 }
237 MRB_RANGE_OUT => {
238 let out = Box::new(Range::Out);
239 let out = Box::into_raw(out);
240 unsafe { sys::mrb_sys_cptr_value(mrb, out.cast::<c_void>()) }
242 }
243 MRB_RANGE_TYPE_MISMATCH => sys::mrb_sys_nil_value(),
244 }
245 }
246}