1use std::borrow::Cow;
2use std::cell::UnsafeCell;
3#[cfg(not(feature = "luau"))]
4use std::ops::{BitOr, BitOrAssign};
5use std::os::raw::c_int;
6
7use ffi::lua_Debug;
8
9use crate::lua::Lua;
10use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
11
12pub struct Debug<'lua> {
22 lua: &'lua Lua,
23 ar: ActivationRecord,
24 #[cfg(feature = "luau")]
25 level: c_int,
26}
27
28impl<'lua> Debug<'lua> {
29 #[cfg(not(feature = "luau"))]
30 pub(crate) fn new(lua: &'lua Lua, ar: *mut lua_Debug) -> Self {
31 Debug {
32 lua,
33 ar: ActivationRecord::Borrowed(ar),
34 }
35 }
36
37 pub(crate) fn new_owned(lua: &'lua Lua, _level: c_int, ar: lua_Debug) -> Self {
38 Debug {
39 lua,
40 ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
41 #[cfg(feature = "luau")]
42 level: _level,
43 }
44 }
45
46 #[cfg(not(feature = "luau"))]
53 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
54 pub fn event(&self) -> DebugEvent {
55 unsafe {
56 match (*self.ar.get()).event {
57 ffi::LUA_HOOKCALL => DebugEvent::Call,
58 ffi::LUA_HOOKRET => DebugEvent::Ret,
59 ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
60 ffi::LUA_HOOKLINE => DebugEvent::Line,
61 ffi::LUA_HOOKCOUNT => DebugEvent::Count,
62 event => DebugEvent::Unknown(event),
63 }
64 }
65 }
66
67 pub fn names(&self) -> DebugNames {
69 unsafe {
70 #[cfg(not(feature = "luau"))]
71 mlua_assert!(
72 ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
73 "lua_getinfo failed with `n`"
74 );
75 #[cfg(feature = "luau")]
76 mlua_assert!(
77 ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
78 "lua_getinfo failed with `n`"
79 );
80
81 DebugNames {
82 name: ptr_to_lossy_str((*self.ar.get()).name),
83 #[cfg(not(feature = "luau"))]
84 name_what: match ptr_to_str((*self.ar.get()).namewhat) {
85 Some("") => None,
86 val => val,
87 },
88 #[cfg(feature = "luau")]
89 name_what: None,
90 }
91 }
92 }
93
94 pub fn source(&self) -> DebugSource {
96 unsafe {
97 #[cfg(not(feature = "luau"))]
98 mlua_assert!(
99 ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
100 "lua_getinfo failed with `S`"
101 );
102 #[cfg(feature = "luau")]
103 mlua_assert!(
104 ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
105 "lua_getinfo failed with `s`"
106 );
107
108 DebugSource {
109 source: ptr_to_lossy_str((*self.ar.get()).source),
110 #[cfg(not(feature = "luau"))]
111 short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
112 #[cfg(feature = "luau")]
113 short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
114 line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
115 #[cfg(not(feature = "luau"))]
116 last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
117 #[cfg(feature = "luau")]
118 last_line_defined: None,
119 what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
120 }
121 }
122 }
123
124 pub fn curr_line(&self) -> i32 {
126 unsafe {
127 #[cfg(not(feature = "luau"))]
128 mlua_assert!(
129 ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
130 "lua_getinfo failed with `l`"
131 );
132 #[cfg(feature = "luau")]
133 mlua_assert!(
134 ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
135 "lua_getinfo failed with `l`"
136 );
137
138 (*self.ar.get()).currentline
139 }
140 }
141
142 #[cfg(not(feature = "luau"))]
145 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
146 pub fn is_tail_call(&self) -> bool {
147 unsafe {
148 mlua_assert!(
149 ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
150 "lua_getinfo failed with `t`"
151 );
152 (*self.ar.get()).currentline != 0
153 }
154 }
155
156 pub fn stack(&self) -> DebugStack {
158 unsafe {
159 #[cfg(not(feature = "luau"))]
160 mlua_assert!(
161 ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
162 "lua_getinfo failed with `u`"
163 );
164 #[cfg(feature = "luau")]
165 mlua_assert!(
166 ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0,
167 "lua_getinfo failed with `a`"
168 );
169
170 #[cfg(not(feature = "luau"))]
171 let stack = DebugStack {
172 num_ups: (*self.ar.get()).nups as _,
173 #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
174 num_params: (*self.ar.get()).nparams as _,
175 #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
176 is_vararg: (*self.ar.get()).isvararg != 0,
177 };
178 #[cfg(feature = "luau")]
179 let stack = DebugStack {
180 num_ups: (*self.ar.get()).nupvals as i32,
181 num_params: (*self.ar.get()).nparams as i32,
182 is_vararg: (*self.ar.get()).isvararg != 0,
183 };
184 stack
185 }
186 }
187}
188
189enum ActivationRecord {
190 #[cfg(not(feature = "luau"))]
191 Borrowed(*mut lua_Debug),
192 Owned(UnsafeCell<lua_Debug>),
193}
194
195impl ActivationRecord {
196 #[inline]
197 fn get(&self) -> *mut lua_Debug {
198 match self {
199 #[cfg(not(feature = "luau"))]
200 ActivationRecord::Borrowed(x) => *x,
201 ActivationRecord::Owned(x) => x.get(),
202 }
203 }
204}
205
206#[derive(Clone, Copy, Debug, PartialEq, Eq)]
208pub enum DebugEvent {
209 Call,
210 Ret,
211 TailCall,
212 Line,
213 Count,
214 Unknown(c_int),
215}
216
217#[derive(Clone, Debug)]
218pub struct DebugNames<'a> {
219 pub name: Option<Cow<'a, str>>,
221 pub name_what: Option<&'static str>,
225}
226
227#[derive(Clone, Debug)]
228pub struct DebugSource<'a> {
229 pub source: Option<Cow<'a, str>>,
231 pub short_src: Option<Cow<'a, str>>,
233 pub line_defined: Option<usize>,
235 pub last_line_defined: Option<usize>,
237 pub what: &'static str,
239}
240
241#[derive(Copy, Clone, Debug)]
242pub struct DebugStack {
243 pub num_ups: i32,
244 #[cfg(any(
246 feature = "lua54",
247 feature = "lua53",
248 feature = "lua52",
249 feature = "luau"
250 ))]
251 pub num_params: i32,
252 #[cfg(any(
254 feature = "lua54",
255 feature = "lua53",
256 feature = "lua52",
257 feature = "luau"
258 ))]
259 pub is_vararg: bool,
260}
261
262#[cfg(not(feature = "luau"))]
264#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
265#[derive(Clone, Copy, Debug, Default)]
266pub struct HookTriggers {
267 pub on_calls: bool,
269 pub on_returns: bool,
271 pub every_line: bool,
273 pub every_nth_instruction: Option<u32>,
280}
281
282#[cfg(not(feature = "luau"))]
283impl HookTriggers {
284 pub const ON_CALLS: Self = HookTriggers::new().on_calls();
286
287 pub const ON_RETURNS: Self = HookTriggers::new().on_returns();
289
290 pub const EVERY_LINE: Self = HookTriggers::new().every_line();
292
293 pub const fn new() -> Self {
295 HookTriggers {
296 on_calls: false,
297 on_returns: false,
298 every_line: false,
299 every_nth_instruction: None,
300 }
301 }
302
303 pub const fn on_calls(mut self) -> Self {
307 self.on_calls = true;
308 self
309 }
310
311 pub const fn on_returns(mut self) -> Self {
315 self.on_returns = true;
316 self
317 }
318
319 pub const fn every_line(mut self) -> Self {
323 self.every_line = true;
324 self
325 }
326
327 pub const fn every_nth_instruction(mut self, n: u32) -> Self {
331 self.every_nth_instruction = Some(n);
332 self
333 }
334
335 pub(crate) const fn mask(&self) -> c_int {
337 let mut mask: c_int = 0;
338 if self.on_calls {
339 mask |= ffi::LUA_MASKCALL
340 }
341 if self.on_returns {
342 mask |= ffi::LUA_MASKRET
343 }
344 if self.every_line {
345 mask |= ffi::LUA_MASKLINE
346 }
347 if self.every_nth_instruction.is_some() {
348 mask |= ffi::LUA_MASKCOUNT
349 }
350 mask
351 }
352
353 pub(crate) const fn count(&self) -> c_int {
356 match self.every_nth_instruction {
357 Some(n) => n as c_int,
358 None => 0,
359 }
360 }
361}
362
363#[cfg(not(feature = "luau"))]
364impl BitOr for HookTriggers {
365 type Output = Self;
366
367 fn bitor(mut self, rhs: Self) -> Self::Output {
368 self.on_calls |= rhs.on_calls;
369 self.on_returns |= rhs.on_returns;
370 self.every_line |= rhs.every_line;
371 if self.every_nth_instruction.is_none() && rhs.every_nth_instruction.is_some() {
372 self.every_nth_instruction = rhs.every_nth_instruction;
373 }
374 self
375 }
376}
377
378#[cfg(not(feature = "luau"))]
379impl BitOrAssign for HookTriggers {
380 fn bitor_assign(&mut self, rhs: Self) {
381 *self = *self | rhs;
382 }
383}