use std::convert::TryFrom;
use crate::{
binding::vm,
rubysys::debug::{self, DebugInspector},
types::{c_int, c_long, c_void, CallbackMutPtr, Value},
};
fn collect_frames<F>(start: usize, limit: usize, fill: F) -> Vec<(Value, c_int)>
where
F: FnOnce(c_int, *mut Value, *mut c_int) -> c_int,
{
let total = c_int::try_from(start.saturating_add(limit)).unwrap_or(c_int::MAX);
let mut frames = vec![Value::from(0); total as usize];
let mut lines: Vec<c_int> = vec![0; total as usize];
let filled = fill(total, frames.as_mut_ptr(), lines.as_mut_ptr()).max(0) as usize;
frames
.into_iter()
.zip(lines)
.take(filled)
.skip(start)
.collect()
}
pub fn profile_frames(start: usize, limit: usize) -> Vec<(Value, c_int)> {
collect_frames(start, limit, |total, frames, lines| unsafe {
debug::rb_profile_frames(0, total, frames, lines)
})
}
#[cfg(ruby_gte_3_3)]
pub fn profile_thread_frames(thread: Value, start: usize, limit: usize) -> Vec<(Value, c_int)> {
collect_frames(start, limit, |total, frames, lines| unsafe {
debug::rb_profile_thread_frames(thread, 0, total, frames, lines)
})
}
pub fn profile_frame_path(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_path(frame) }
}
pub fn profile_frame_absolute_path(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_absolute_path(frame) }
}
pub fn profile_frame_label(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_label(frame) }
}
pub fn profile_frame_base_label(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_base_label(frame) }
}
pub fn profile_frame_full_label(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_full_label(frame) }
}
pub fn profile_frame_first_lineno(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_first_lineno(frame) }
}
pub fn profile_frame_classpath(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_classpath(frame) }
}
pub fn profile_frame_is_singleton_method(frame: Value) -> bool {
unsafe { debug::rb_profile_frame_singleton_method_p(frame) }.is_true()
}
pub fn profile_frame_method_name(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_method_name(frame) }
}
pub fn profile_frame_qualified_method_name(frame: Value) -> Value {
unsafe { debug::rb_profile_frame_qualified_method_name(frame) }
}
rutie_callback! {
fn debug_inspector_callback<F>(dc: *const DebugInspector, data: CallbackMutPtr) -> Value
where
F: FnOnce(*const DebugInspector) -> Value,
{
match unsafe { (*(data as *mut Option<F>)).take() } {
Some(func) => vm::call_catching_panic(move || func(dc)),
None => Value::from(0),
}
}
}
pub fn debug_inspector_open<F>(func: F) -> Value
where
F: FnOnce(*const DebugInspector) -> Value,
{
let mut func = Some(func);
unsafe {
debug::rb_debug_inspector_open(
debug_inspector_callback::<F>,
&mut func as *mut Option<F> as *mut c_void,
)
}
}
pub unsafe fn debug_inspector_backtrace_locations(dc: *const DebugInspector) -> Value {
debug::rb_debug_inspector_backtrace_locations(dc)
}
pub unsafe fn debug_inspector_frame_self(dc: *const DebugInspector, index: usize) -> Value {
debug::rb_debug_inspector_frame_self_get(dc, index as c_long)
}
pub unsafe fn debug_inspector_frame_class(dc: *const DebugInspector, index: usize) -> Value {
debug::rb_debug_inspector_frame_class_get(dc, index as c_long)
}
pub unsafe fn debug_inspector_frame_binding(dc: *const DebugInspector, index: usize) -> Value {
debug::rb_debug_inspector_frame_binding_get(dc, index as c_long)
}
pub unsafe fn debug_inspector_frame_iseq(dc: *const DebugInspector, index: usize) -> Value {
debug::rb_debug_inspector_frame_iseq_get(dc, index as c_long)
}
#[cfg(ruby_gte_3_2)]
pub unsafe fn debug_inspector_frame_depth(dc: *const DebugInspector, index: usize) -> Value {
debug::rb_debug_inspector_frame_depth(dc, index as c_long)
}
#[cfg(ruby_gte_3_2)]
pub fn debug_inspector_current_depth() -> Value {
unsafe { debug::rb_debug_inspector_current_depth() }
}
#[cfg(ruby_gte_3_3)]
rutie_callback! {
fn postponed_job_callback<F>(data: *mut c_void)
where
F: Fn() + Send + Sync + 'static,
{
let func = unsafe { &*(data as *const F) };
let _ = vm::protect_value(|| {
func();
Value::from(crate::binding::global::RubySpecialConsts::Nil as crate::types::InternalValue)
});
}
}
#[cfg(ruby_gte_3_3)]
pub fn postponed_job_preregister<F>(func: F) -> Option<debug::PostponedJobHandle>
where
F: Fn() + Send + Sync + 'static,
{
let data = Box::into_raw(Box::new(func));
let handle = unsafe {
debug::rb_postponed_job_preregister(0, postponed_job_callback::<F>, data as *mut c_void)
};
if handle == debug::POSTPONED_JOB_HANDLE_INVALID {
drop(unsafe { Box::from_raw(data) });
None
} else {
Some(handle)
}
}
#[cfg(ruby_gte_3_3)]
pub unsafe fn postponed_job_trigger(handle: debug::PostponedJobHandle) {
debug::rb_postponed_job_trigger(handle)
}