mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
c3822219e0
This feature works on lua54, lua53, lua52 and lua51 only. LuaJIT is unstable.
205 lines
6.3 KiB
Rust
205 lines
6.3 KiB
Rust
#![cfg_attr(
|
|
not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51")),
|
|
allow(dead_code)
|
|
)]
|
|
|
|
use std::ffi::CStr;
|
|
use std::marker::PhantomData;
|
|
use std::os::raw::{c_char, c_int};
|
|
|
|
use crate::ffi::{self, lua_Debug, lua_State};
|
|
use crate::lua::Lua;
|
|
use crate::util::callback_error;
|
|
|
|
/// Contains information about currently executing Lua code.
|
|
///
|
|
/// The `Debug` structure is provided as a parameter to the hook function set with
|
|
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
|
/// Lua code executing at the time that the hook function was called. Further information can be
|
|
/// found in the [Lua 5.3 documentaton][lua_doc].
|
|
///
|
|
/// Requires `feature = "lua54/lua53/lua52/lua51"`
|
|
///
|
|
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
|
|
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
|
|
#[derive(Clone)]
|
|
pub struct Debug<'a> {
|
|
ar: *mut lua_Debug,
|
|
state: *mut lua_State,
|
|
_phantom: PhantomData<&'a ()>,
|
|
}
|
|
|
|
impl<'a> Debug<'a> {
|
|
/// Corresponds to the `n` what mask.
|
|
pub fn names(&self) -> DebugNames<'a> {
|
|
unsafe {
|
|
mlua_assert!(
|
|
ffi::lua_getinfo(self.state, cstr!("n"), self.ar) != 0,
|
|
"lua_getinfo failed with `n`"
|
|
);
|
|
DebugNames {
|
|
name: ptr_to_str((*self.ar).name),
|
|
name_what: ptr_to_str((*self.ar).namewhat),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Corresponds to the `n` what mask.
|
|
pub fn source(&self) -> DebugSource<'a> {
|
|
unsafe {
|
|
mlua_assert!(
|
|
ffi::lua_getinfo(self.state, cstr!("S"), self.ar) != 0,
|
|
"lua_getinfo failed with `S`"
|
|
);
|
|
DebugSource {
|
|
source: ptr_to_str((*self.ar).source),
|
|
short_src: ptr_to_str((*self.ar).short_src.as_ptr()),
|
|
line_defined: (*self.ar).linedefined as i32,
|
|
last_line_defined: (*self.ar).lastlinedefined as i32,
|
|
what: ptr_to_str((*self.ar).what),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Corresponds to the `l` what mask. Returns the current line.
|
|
pub fn curr_line(&self) -> i32 {
|
|
unsafe {
|
|
mlua_assert!(
|
|
ffi::lua_getinfo(self.state, cstr!("l"), self.ar) != 0,
|
|
"lua_getinfo failed with `l`"
|
|
);
|
|
(*self.ar).currentline as i32
|
|
}
|
|
}
|
|
|
|
/// Corresponds to the `t` what mask. Returns true if the hook is in a function tail call, false
|
|
/// otherwise.
|
|
pub fn is_tail_call(&self) -> bool {
|
|
unsafe {
|
|
mlua_assert!(
|
|
ffi::lua_getinfo(self.state, cstr!("t"), self.ar) != 0,
|
|
"lua_getinfo failed with `t`"
|
|
);
|
|
(*self.ar).currentline != 0
|
|
}
|
|
}
|
|
|
|
/// Corresponds to the `u` what mask.
|
|
pub fn stack(&self) -> DebugStack {
|
|
unsafe {
|
|
mlua_assert!(
|
|
ffi::lua_getinfo(self.state, cstr!("u"), self.ar) != 0,
|
|
"lua_getinfo failed with `u`"
|
|
);
|
|
DebugStack {
|
|
num_ups: (*self.ar).nups as i32,
|
|
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
|
|
num_params: (*self.ar).nparams as i32,
|
|
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
|
|
is_vararg: (*self.ar).isvararg != 0,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct DebugNames<'a> {
|
|
pub name: Option<&'a [u8]>,
|
|
pub name_what: Option<&'a [u8]>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct DebugSource<'a> {
|
|
pub source: Option<&'a [u8]>,
|
|
pub short_src: Option<&'a [u8]>,
|
|
pub line_defined: i32,
|
|
pub last_line_defined: i32,
|
|
pub what: Option<&'a [u8]>,
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct DebugStack {
|
|
pub num_ups: i32,
|
|
/// Requires `feature = "lua54/lua53/lua52"`
|
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
|
pub num_params: i32,
|
|
/// Requires `feature = "lua54/lua53/lua52"`
|
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
|
pub is_vararg: bool,
|
|
}
|
|
|
|
/// Determines when a hook function will be called by Lua.
|
|
///
|
|
/// Requires `feature = "lua54/lua53/lua52/lua51"`
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
pub struct HookTriggers {
|
|
/// Before a function call.
|
|
pub on_calls: bool,
|
|
/// When Lua returns from a function.
|
|
pub on_returns: bool,
|
|
/// Before executing a new line, or returning from a function call.
|
|
pub every_line: bool,
|
|
/// After a certain number of VM instructions have been executed. When set to `Some(count)`,
|
|
/// `count` is the number of VM instructions to execute before calling the hook.
|
|
///
|
|
/// # Performance
|
|
///
|
|
/// Setting this option to a low value can incur a very high overhead.
|
|
pub every_nth_instruction: Option<u32>,
|
|
}
|
|
|
|
impl HookTriggers {
|
|
// Compute the mask to pass to `lua_sethook`.
|
|
pub(crate) fn mask(&self) -> c_int {
|
|
let mut mask: c_int = 0;
|
|
if self.on_calls {
|
|
mask |= ffi::LUA_MASKCALL
|
|
}
|
|
if self.on_returns {
|
|
mask |= ffi::LUA_MASKRET
|
|
}
|
|
if self.every_line {
|
|
mask |= ffi::LUA_MASKLINE
|
|
}
|
|
if self.every_nth_instruction.is_some() {
|
|
mask |= ffi::LUA_MASKCOUNT
|
|
}
|
|
mask
|
|
}
|
|
|
|
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
|
// returned.
|
|
pub(crate) fn count(&self) -> c_int {
|
|
self.every_nth_instruction.unwrap_or(0) as c_int
|
|
}
|
|
}
|
|
|
|
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
|
|
callback_error(state, |_| {
|
|
let debug = Debug {
|
|
ar,
|
|
state,
|
|
_phantom: PhantomData,
|
|
};
|
|
|
|
let lua = Lua::make_from_ptr(state);
|
|
let hook_cb = mlua_expect!(lua.hook_callback(), "no hook callback set in hook_proc");
|
|
|
|
#[allow(clippy::match_wild_err_arm)]
|
|
match hook_cb.try_borrow_mut() {
|
|
Ok(mut b) => (&mut *b)(&lua, debug),
|
|
Err(_) => mlua_panic!("Lua should not allow hooks to be called within another hook"),
|
|
}?;
|
|
|
|
Ok(())
|
|
});
|
|
}
|
|
|
|
unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> {
|
|
if input.is_null() {
|
|
None
|
|
} else {
|
|
Some(CStr::from_ptr(input).to_bytes())
|
|
}
|
|
}
|