mlua_codemp_patch/
hook.rs

1use std::borrow::Cow;
2use std::cell::UnsafeCell;
3use std::ops::Deref;
4#[cfg(not(feature = "luau"))]
5use std::ops::{BitOr, BitOrAssign};
6use std::os::raw::c_int;
7
8use ffi::lua_Debug;
9
10use crate::state::RawLua;
11use crate::types::ReentrantMutexGuard;
12use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
13
14/// Contains information about currently executing Lua code.
15///
16/// The `Debug` structure is provided as a parameter to the hook function set with
17/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
18/// Lua code executing at the time that the hook function was called. Further information can be
19/// found in the Lua [documentation][lua_doc].
20///
21/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
22/// [`Lua::set_hook`]: crate::Lua::set_hook
23pub struct Debug<'a> {
24    lua: EitherLua<'a>,
25    ar: ActivationRecord,
26    #[cfg(feature = "luau")]
27    level: c_int,
28}
29
30enum EitherLua<'a> {
31    Owned(ReentrantMutexGuard<'a, RawLua>),
32    #[cfg(not(feature = "luau"))]
33    Borrowed(&'a RawLua),
34}
35
36impl Deref for EitherLua<'_> {
37    type Target = RawLua;
38
39    fn deref(&self) -> &Self::Target {
40        match self {
41            EitherLua::Owned(guard) => guard,
42            #[cfg(not(feature = "luau"))]
43            EitherLua::Borrowed(lua) => lua,
44        }
45    }
46}
47
48impl<'a> Debug<'a> {
49    // We assume the lock is held when this function is called.
50    #[cfg(not(feature = "luau"))]
51    pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
52        Debug {
53            lua: EitherLua::Borrowed(lua),
54            ar: ActivationRecord::Borrowed(ar),
55        }
56    }
57
58    pub(crate) fn new_owned(guard: ReentrantMutexGuard<'a, RawLua>, _level: c_int, ar: lua_Debug) -> Self {
59        Debug {
60            lua: EitherLua::Owned(guard),
61            ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
62            #[cfg(feature = "luau")]
63            level: _level,
64        }
65    }
66
67    /// Returns the specific event that triggered the hook.
68    ///
69    /// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
70    /// from a function that did a tail call.
71    ///
72    /// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
73    #[cfg(not(feature = "luau"))]
74    #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
75    pub fn event(&self) -> DebugEvent {
76        unsafe {
77            match (*self.ar.get()).event {
78                ffi::LUA_HOOKCALL => DebugEvent::Call,
79                ffi::LUA_HOOKRET => DebugEvent::Ret,
80                ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
81                ffi::LUA_HOOKLINE => DebugEvent::Line,
82                ffi::LUA_HOOKCOUNT => DebugEvent::Count,
83                event => DebugEvent::Unknown(event),
84            }
85        }
86    }
87
88    /// Corresponds to the `n` what mask.
89    pub fn names(&self) -> DebugNames {
90        unsafe {
91            #[cfg(not(feature = "luau"))]
92            mlua_assert!(
93                ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
94                "lua_getinfo failed with `n`"
95            );
96            #[cfg(feature = "luau")]
97            mlua_assert!(
98                ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
99                "lua_getinfo failed with `n`"
100            );
101
102            DebugNames {
103                name: ptr_to_lossy_str((*self.ar.get()).name),
104                #[cfg(not(feature = "luau"))]
105                name_what: match ptr_to_str((*self.ar.get()).namewhat) {
106                    Some("") => None,
107                    val => val,
108                },
109                #[cfg(feature = "luau")]
110                name_what: None,
111            }
112        }
113    }
114
115    /// Corresponds to the `S` what mask.
116    pub fn source(&self) -> DebugSource {
117        unsafe {
118            #[cfg(not(feature = "luau"))]
119            mlua_assert!(
120                ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
121                "lua_getinfo failed with `S`"
122            );
123            #[cfg(feature = "luau")]
124            mlua_assert!(
125                ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
126                "lua_getinfo failed with `s`"
127            );
128
129            DebugSource {
130                source: ptr_to_lossy_str((*self.ar.get()).source),
131                #[cfg(not(feature = "luau"))]
132                short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
133                #[cfg(feature = "luau")]
134                short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
135                line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
136                #[cfg(not(feature = "luau"))]
137                last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
138                #[cfg(feature = "luau")]
139                last_line_defined: None,
140                what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
141            }
142        }
143    }
144
145    /// Corresponds to the `l` what mask. Returns the current line.
146    pub fn curr_line(&self) -> i32 {
147        unsafe {
148            #[cfg(not(feature = "luau"))]
149            mlua_assert!(
150                ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
151                "lua_getinfo failed with `l`"
152            );
153            #[cfg(feature = "luau")]
154            mlua_assert!(
155                ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
156                "lua_getinfo failed with `l`"
157            );
158
159            (*self.ar.get()).currentline
160        }
161    }
162
163    /// Corresponds to the `t` what mask. Returns true if the hook is in a function tail call, false
164    /// otherwise.
165    #[cfg(not(feature = "luau"))]
166    #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
167    pub fn is_tail_call(&self) -> bool {
168        unsafe {
169            mlua_assert!(
170                ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
171                "lua_getinfo failed with `t`"
172            );
173            (*self.ar.get()).currentline != 0
174        }
175    }
176
177    /// Corresponds to the `u` what mask.
178    pub fn stack(&self) -> DebugStack {
179        unsafe {
180            #[cfg(not(feature = "luau"))]
181            mlua_assert!(
182                ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
183                "lua_getinfo failed with `u`"
184            );
185            #[cfg(feature = "luau")]
186            mlua_assert!(
187                ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0,
188                "lua_getinfo failed with `a`"
189            );
190
191            #[cfg(not(feature = "luau"))]
192            let stack = DebugStack {
193                num_ups: (*self.ar.get()).nups as _,
194                #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
195                num_params: (*self.ar.get()).nparams as _,
196                #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
197                is_vararg: (*self.ar.get()).isvararg != 0,
198            };
199            #[cfg(feature = "luau")]
200            let stack = DebugStack {
201                num_ups: (*self.ar.get()).nupvals as i32,
202                num_params: (*self.ar.get()).nparams as i32,
203                is_vararg: (*self.ar.get()).isvararg != 0,
204            };
205            stack
206        }
207    }
208}
209
210enum ActivationRecord {
211    #[cfg(not(feature = "luau"))]
212    Borrowed(*mut lua_Debug),
213    Owned(UnsafeCell<lua_Debug>),
214}
215
216impl ActivationRecord {
217    #[inline]
218    fn get(&self) -> *mut lua_Debug {
219        match self {
220            #[cfg(not(feature = "luau"))]
221            ActivationRecord::Borrowed(x) => *x,
222            ActivationRecord::Owned(x) => x.get(),
223        }
224    }
225}
226
227/// Represents a specific event that triggered the hook.
228#[derive(Clone, Copy, Debug, PartialEq, Eq)]
229pub enum DebugEvent {
230    Call,
231    Ret,
232    TailCall,
233    Line,
234    Count,
235    Unknown(c_int),
236}
237
238#[derive(Clone, Debug)]
239pub struct DebugNames<'a> {
240    /// A (reasonable) name of the function (`None` if the name cannot be found).
241    pub name: Option<Cow<'a, str>>,
242    /// Explains the `name` field (can be `global`/`local`/`method`/`field`/`upvalue`/etc).
243    ///
244    /// Always `None` for Luau.
245    pub name_what: Option<&'static str>,
246}
247
248#[derive(Clone, Debug)]
249pub struct DebugSource<'a> {
250    /// Source of the chunk that created the function.
251    pub source: Option<Cow<'a, str>>,
252    /// A "printable" version of `source`, to be used in error messages.
253    pub short_src: Option<Cow<'a, str>>,
254    /// The line number where the definition of the function starts.
255    pub line_defined: Option<usize>,
256    /// The line number where the definition of the function ends (not set by Luau).
257    pub last_line_defined: Option<usize>,
258    /// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
259    /// the main part of a chunk.
260    pub what: &'static str,
261}
262
263#[derive(Copy, Clone, Debug)]
264pub struct DebugStack {
265    pub num_ups: i32,
266    /// Requires `feature = "lua54/lua53/lua52/luau"`
267    #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
268    pub num_params: i32,
269    /// Requires `feature = "lua54/lua53/lua52/luau"`
270    #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
271    pub is_vararg: bool,
272}
273
274/// Determines when a hook function will be called by Lua.
275#[cfg(not(feature = "luau"))]
276#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
277#[derive(Clone, Copy, Debug, Default)]
278pub struct HookTriggers {
279    /// Before a function call.
280    pub on_calls: bool,
281    /// When Lua returns from a function.
282    pub on_returns: bool,
283    /// Before executing a new line, or returning from a function call.
284    pub every_line: bool,
285    /// After a certain number of VM instructions have been executed. When set to `Some(count)`,
286    /// `count` is the number of VM instructions to execute before calling the hook.
287    ///
288    /// # Performance
289    ///
290    /// Setting this option to a low value can incur a very high overhead.
291    pub every_nth_instruction: Option<u32>,
292}
293
294#[cfg(not(feature = "luau"))]
295impl HookTriggers {
296    /// An instance of `HookTriggers` with `on_calls` trigger set.
297    pub const ON_CALLS: Self = HookTriggers::new().on_calls();
298
299    /// An instance of `HookTriggers` with `on_returns` trigger set.
300    pub const ON_RETURNS: Self = HookTriggers::new().on_returns();
301
302    /// An instance of `HookTriggers` with `every_line` trigger set.
303    pub const EVERY_LINE: Self = HookTriggers::new().every_line();
304
305    /// Returns a new instance of `HookTriggers` with all triggers disabled.
306    pub const fn new() -> Self {
307        HookTriggers {
308            on_calls: false,
309            on_returns: false,
310            every_line: false,
311            every_nth_instruction: None,
312        }
313    }
314
315    /// Returns an instance of `HookTriggers` with [`on_calls`] trigger set.
316    ///
317    /// [`on_calls`]: #structfield.on_calls
318    pub const fn on_calls(mut self) -> Self {
319        self.on_calls = true;
320        self
321    }
322
323    /// Returns an instance of `HookTriggers` with [`on_returns`] trigger set.
324    ///
325    /// [`on_returns`]: #structfield.on_returns
326    pub const fn on_returns(mut self) -> Self {
327        self.on_returns = true;
328        self
329    }
330
331    /// Returns an instance of `HookTriggers` with [`every_line`] trigger set.
332    ///
333    /// [`every_line`]: #structfield.every_line
334    pub const fn every_line(mut self) -> Self {
335        self.every_line = true;
336        self
337    }
338
339    /// Returns an instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
340    ///
341    /// [`every_nth_instruction`]: #structfield.every_nth_instruction
342    pub const fn every_nth_instruction(mut self, n: u32) -> Self {
343        self.every_nth_instruction = Some(n);
344        self
345    }
346
347    // Compute the mask to pass to `lua_sethook`.
348    pub(crate) const fn mask(&self) -> c_int {
349        let mut mask: c_int = 0;
350        if self.on_calls {
351            mask |= ffi::LUA_MASKCALL
352        }
353        if self.on_returns {
354            mask |= ffi::LUA_MASKRET
355        }
356        if self.every_line {
357            mask |= ffi::LUA_MASKLINE
358        }
359        if self.every_nth_instruction.is_some() {
360            mask |= ffi::LUA_MASKCOUNT
361        }
362        mask
363    }
364
365    // Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
366    // returned.
367    pub(crate) const fn count(&self) -> c_int {
368        match self.every_nth_instruction {
369            Some(n) => n as c_int,
370            None => 0,
371        }
372    }
373}
374
375#[cfg(not(feature = "luau"))]
376impl BitOr for HookTriggers {
377    type Output = Self;
378
379    fn bitor(mut self, rhs: Self) -> Self::Output {
380        self.on_calls |= rhs.on_calls;
381        self.on_returns |= rhs.on_returns;
382        self.every_line |= rhs.every_line;
383        if self.every_nth_instruction.is_none() && rhs.every_nth_instruction.is_some() {
384            self.every_nth_instruction = rhs.every_nth_instruction;
385        }
386        self
387    }
388}
389
390#[cfg(not(feature = "luau"))]
391impl BitOrAssign for HookTriggers {
392    fn bitor_assign(&mut self, rhs: Self) {
393        *self = *self | rhs;
394    }
395}