1use std::borrow::Cow;
8use std::os::raw::c_int;
9
10use ffi::{lua_Debug, lua_State};
11
12use crate::function::Function;
13use crate::state::RawLua;
14use crate::util::{StackGuard, assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
15
16pub struct Debug<'a> {
23 state: *mut lua_State,
24 lua: &'a RawLua,
25 #[cfg_attr(not(feature = "luau"), allow(unused))]
26 level: c_int,
27 ar: *mut lua_Debug,
28}
29
30impl<'a> Debug<'a> {
31 pub(crate) fn new(lua: &'a RawLua, level: c_int, ar: *mut lua_Debug) -> Self {
32 Debug {
33 state: lua.state(),
34 lua,
35 ar,
36 level,
37 }
38 }
39
40 #[cfg(not(feature = "luau"))]
47 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
48 pub fn event(&self) -> DebugEvent {
49 unsafe {
50 match (*self.ar).event {
51 ffi::LUA_HOOKCALL => DebugEvent::Call,
52 ffi::LUA_HOOKRET => DebugEvent::Ret,
53 ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
54 ffi::LUA_HOOKLINE => DebugEvent::Line,
55 ffi::LUA_HOOKCOUNT => DebugEvent::Count,
56 event => DebugEvent::Unknown(event),
57 }
58 }
59 }
60
61 pub fn function(&self) -> Function {
65 unsafe {
66 let _sg = StackGuard::new(self.state);
67 assert_stack(self.state, 1);
68
69 #[cfg(not(feature = "luau"))]
70 mlua_assert!(
71 ffi::lua_getinfo(self.state, cstr!("f"), self.ar) != 0,
72 "lua_getinfo failed with `f`"
73 );
74 #[cfg(feature = "luau")]
75 mlua_assert!(
76 ffi::lua_getinfo(self.state, self.level, cstr!("f"), self.ar) != 0,
77 "lua_getinfo failed with `f`"
78 );
79
80 ffi::lua_xmove(self.state, self.lua.ref_thread(), 1);
81 Function(self.lua.pop_ref_thread())
82 }
83 }
84
85 pub fn names(&self) -> DebugNames<'_> {
87 unsafe {
88 #[cfg(not(feature = "luau"))]
89 mlua_assert!(
90 ffi::lua_getinfo(self.state, cstr!("n"), self.ar) != 0,
91 "lua_getinfo failed with `n`"
92 );
93 #[cfg(feature = "luau")]
94 mlua_assert!(
95 ffi::lua_getinfo(self.state, self.level, cstr!("n"), self.ar) != 0,
96 "lua_getinfo failed with `n`"
97 );
98
99 DebugNames {
100 name: ptr_to_lossy_str((*self.ar).name),
101 #[cfg(not(feature = "luau"))]
102 name_what: match ptr_to_str((*self.ar).namewhat) {
103 Some("") => None,
104 val => val,
105 },
106 #[cfg(feature = "luau")]
107 name_what: None,
108 }
109 }
110 }
111
112 pub fn source(&self) -> DebugSource<'_> {
114 unsafe {
115 #[cfg(not(feature = "luau"))]
116 mlua_assert!(
117 ffi::lua_getinfo(self.state, cstr!("S"), self.ar) != 0,
118 "lua_getinfo failed with `S`"
119 );
120 #[cfg(feature = "luau")]
121 mlua_assert!(
122 ffi::lua_getinfo(self.state, self.level, cstr!("s"), self.ar) != 0,
123 "lua_getinfo failed with `s`"
124 );
125
126 DebugSource {
127 source: ptr_to_lossy_str((*self.ar).source),
128 #[cfg(not(feature = "luau"))]
129 short_src: ptr_to_lossy_str((*self.ar).short_src.as_ptr()),
130 #[cfg(feature = "luau")]
131 short_src: ptr_to_lossy_str((*self.ar).short_src),
132 line_defined: linenumber_to_usize((*self.ar).linedefined),
133 #[cfg(not(feature = "luau"))]
134 last_line_defined: linenumber_to_usize((*self.ar).lastlinedefined),
135 #[cfg(feature = "luau")]
136 last_line_defined: None,
137 what: ptr_to_str((*self.ar).what).unwrap_or("main"),
138 }
139 }
140 }
141
142 pub fn current_line(&self) -> Option<usize> {
144 unsafe {
145 #[cfg(not(feature = "luau"))]
146 mlua_assert!(
147 ffi::lua_getinfo(self.state, cstr!("l"), self.ar) != 0,
148 "lua_getinfo failed with `l`"
149 );
150 #[cfg(feature = "luau")]
151 mlua_assert!(
152 ffi::lua_getinfo(self.state, self.level, cstr!("l"), self.ar) != 0,
153 "lua_getinfo failed with `l`"
154 );
155
156 linenumber_to_usize((*self.ar).currentline)
157 }
158 }
159
160 #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
163 #[cfg_attr(
164 docsrs,
165 doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52")))
166 )]
167 pub fn is_tail_call(&self) -> bool {
168 unsafe {
169 mlua_assert!(
170 ffi::lua_getinfo(self.state, cstr!("t"), self.ar) != 0,
171 "lua_getinfo failed with `t`"
172 );
173 (*self.ar).istailcall != 0
174 }
175 }
176
177 pub fn stack(&self) -> DebugStack {
179 unsafe {
180 #[cfg(not(feature = "luau"))]
181 mlua_assert!(
182 ffi::lua_getinfo(self.state, cstr!("u"), self.ar) != 0,
183 "lua_getinfo failed with `u`"
184 );
185 #[cfg(feature = "luau")]
186 mlua_assert!(
187 ffi::lua_getinfo(self.state, self.level, cstr!("au"), self.ar) != 0,
188 "lua_getinfo failed with `au`"
189 );
190
191 #[cfg(not(feature = "luau"))]
192 let stack = DebugStack {
193 num_upvalues: (*self.ar).nups as _,
194 #[cfg(not(any(feature = "lua51", feature = "luajit")))]
195 num_params: (*self.ar).nparams as _,
196 #[cfg(not(any(feature = "lua51", feature = "luajit")))]
197 is_vararg: (*self.ar).isvararg != 0,
198 };
199 #[cfg(feature = "luau")]
200 let stack = DebugStack {
201 num_upvalues: (*self.ar).nupvals,
202 num_params: (*self.ar).nparams,
203 is_vararg: (*self.ar).isvararg != 0,
204 };
205 stack
206 }
207 }
208}
209
210#[cfg(not(feature = "luau"))]
212#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
213#[derive(Clone, Copy, Debug, PartialEq, Eq)]
214pub enum DebugEvent {
215 Call,
216 Ret,
217 TailCall,
218 Line,
219 Count,
220 Unknown(c_int),
221}
222
223#[derive(Clone, Debug)]
227pub struct DebugNames<'a> {
228 pub name: Option<Cow<'a, str>>,
230 pub name_what: Option<&'static str>,
234}
235
236#[derive(Clone, Debug)]
240pub struct DebugSource<'a> {
241 pub source: Option<Cow<'a, str>>,
243 pub short_src: Option<Cow<'a, str>>,
245 pub line_defined: Option<usize>,
247 pub last_line_defined: Option<usize>,
249 pub what: &'static str,
252}
253
254#[derive(Copy, Clone, Debug)]
258pub struct DebugStack {
259 pub num_upvalues: u8,
261 #[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
263 #[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
264 pub num_params: u8,
265 #[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
267 #[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
268 pub is_vararg: bool,
269}
270
271#[cfg(not(feature = "luau"))]
273#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
274#[derive(Clone, Copy, Debug, Default)]
275pub struct HookTriggers {
276 pub on_calls: bool,
278 pub on_returns: bool,
280 pub every_line: bool,
282 pub every_nth_instruction: Option<u32>,
289}
290
291#[cfg(not(feature = "luau"))]
292impl HookTriggers {
293 pub const ON_CALLS: Self = HookTriggers::new().on_calls();
295
296 pub const ON_RETURNS: Self = HookTriggers::new().on_returns();
298
299 pub const EVERY_LINE: Self = HookTriggers::new().every_line();
301
302 pub const fn new() -> Self {
304 HookTriggers {
305 on_calls: false,
306 on_returns: false,
307 every_line: false,
308 every_nth_instruction: None,
309 }
310 }
311
312 pub const fn on_calls(mut self) -> Self {
316 self.on_calls = true;
317 self
318 }
319
320 pub const fn on_returns(mut self) -> Self {
324 self.on_returns = true;
325 self
326 }
327
328 pub const fn every_line(mut self) -> Self {
332 self.every_line = true;
333 self
334 }
335
336 pub const fn every_nth_instruction(mut self, n: u32) -> Self {
340 self.every_nth_instruction = Some(n);
341 self
342 }
343
344 #[cfg(not(feature = "luau"))]
346 pub(crate) const fn mask(&self) -> c_int {
347 let mut mask: c_int = 0;
348 if self.on_calls {
349 mask |= ffi::LUA_MASKCALL
350 }
351 if self.on_returns {
352 mask |= ffi::LUA_MASKRET
353 }
354 if self.every_line {
355 mask |= ffi::LUA_MASKLINE
356 }
357 if self.every_nth_instruction.is_some() {
358 mask |= ffi::LUA_MASKCOUNT
359 }
360 mask
361 }
362
363 #[cfg(not(feature = "luau"))]
366 pub(crate) const fn count(&self) -> c_int {
367 match self.every_nth_instruction {
368 Some(n) => n as c_int,
369 None => 0,
370 }
371 }
372}
373
374#[cfg(not(feature = "luau"))]
375impl std::ops::BitOr for HookTriggers {
376 type Output = Self;
377
378 fn bitor(mut self, rhs: Self) -> Self::Output {
379 self.on_calls |= rhs.on_calls;
380 self.on_returns |= rhs.on_returns;
381 self.every_line |= rhs.every_line;
382 if self.every_nth_instruction.is_none() && rhs.every_nth_instruction.is_some() {
383 self.every_nth_instruction = rhs.every_nth_instruction;
384 }
385 self
386 }
387}
388
389#[cfg(not(feature = "luau"))]
390impl std::ops::BitOrAssign for HookTriggers {
391 fn bitor_assign(&mut self, rhs: Self) {
392 *self = *self | rhs;
393 }
394}