1use std::cell::UnsafeCell;
2#[cfg(not(feature = "luau"))]
3use std::ops::{BitOr, BitOrAssign};
4use std::os::raw::c_int;
5
6use crate::ffi::{self, lua_Debug};
7use crate::lua::Lua;
8use crate::util::ptr_to_cstr_bytes;
9
10pub struct Debug<'lua> {
20 lua: &'lua Lua,
21 ar: ActivationRecord,
22 #[cfg(feature = "luau")]
23 level: c_int,
24}
25
26impl<'lua> Debug<'lua> {
27 #[cfg(not(feature = "luau"))]
28 pub(crate) fn new(lua: &'lua Lua, ar: *mut lua_Debug) -> Self {
29 Debug {
30 lua,
31 ar: ActivationRecord::Borrowed(ar),
32 }
33 }
34
35 pub(crate) fn new_owned(lua: &'lua Lua, _level: c_int, ar: lua_Debug) -> Self {
36 Debug {
37 lua,
38 ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
39 #[cfg(feature = "luau")]
40 level: _level,
41 }
42 }
43
44 #[cfg(not(feature = "luau"))]
51 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
52 pub fn event(&self) -> DebugEvent {
53 unsafe {
54 match (*self.ar.get()).event {
55 ffi::LUA_HOOKCALL => DebugEvent::Call,
56 ffi::LUA_HOOKRET => DebugEvent::Ret,
57 ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
58 ffi::LUA_HOOKLINE => DebugEvent::Line,
59 ffi::LUA_HOOKCOUNT => DebugEvent::Count,
60 event => DebugEvent::Unknown(event),
61 }
62 }
63 }
64
65 pub fn names(&self) -> DebugNames<'lua> {
67 unsafe {
68 #[cfg(not(feature = "luau"))]
69 mlua_assert!(
70 ffi::lua_getinfo(self.lua.state, cstr!("n"), self.ar.get()) != 0,
71 "lua_getinfo failed with `n`"
72 );
73 #[cfg(feature = "luau")]
74 mlua_assert!(
75 ffi::lua_getinfo(self.lua.state, self.level, cstr!("n"), self.ar.get()) != 0,
76 "lua_getinfo failed with `n`"
77 );
78
79 DebugNames {
80 name: ptr_to_cstr_bytes((*self.ar.get()).name),
81 #[cfg(not(feature = "luau"))]
82 name_what: ptr_to_cstr_bytes((*self.ar.get()).namewhat),
83 #[cfg(feature = "luau")]
84 name_what: None,
85 }
86 }
87 }
88
89 pub fn source(&self) -> DebugSource<'lua> {
91 unsafe {
92 #[cfg(not(feature = "luau"))]
93 mlua_assert!(
94 ffi::lua_getinfo(self.lua.state, cstr!("S"), self.ar.get()) != 0,
95 "lua_getinfo failed with `S`"
96 );
97 #[cfg(feature = "luau")]
98 mlua_assert!(
99 ffi::lua_getinfo(self.lua.state, self.level, cstr!("s"), self.ar.get()) != 0,
100 "lua_getinfo failed with `s`"
101 );
102
103 DebugSource {
104 source: ptr_to_cstr_bytes((*self.ar.get()).source),
105 short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
106 line_defined: (*self.ar.get()).linedefined as i32,
107 #[cfg(not(feature = "luau"))]
108 last_line_defined: (*self.ar.get()).lastlinedefined as i32,
109 what: ptr_to_cstr_bytes((*self.ar.get()).what),
110 }
111 }
112 }
113
114 pub fn curr_line(&self) -> i32 {
116 unsafe {
117 #[cfg(not(feature = "luau"))]
118 mlua_assert!(
119 ffi::lua_getinfo(self.lua.state, cstr!("l"), self.ar.get()) != 0,
120 "lua_getinfo failed with `l`"
121 );
122 #[cfg(feature = "luau")]
123 mlua_assert!(
124 ffi::lua_getinfo(self.lua.state, self.level, cstr!("l"), self.ar.get()) != 0,
125 "lua_getinfo failed with `l`"
126 );
127
128 (*self.ar.get()).currentline as i32
129 }
130 }
131
132 #[cfg(not(feature = "luau"))]
135 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
136 pub fn is_tail_call(&self) -> bool {
137 unsafe {
138 mlua_assert!(
139 ffi::lua_getinfo(self.lua.state, cstr!("t"), self.ar.get()) != 0,
140 "lua_getinfo failed with `t`"
141 );
142 (*self.ar.get()).currentline != 0
143 }
144 }
145
146 pub fn stack(&self) -> DebugStack {
148 unsafe {
149 #[cfg(not(feature = "luau"))]
150 mlua_assert!(
151 ffi::lua_getinfo(self.lua.state, cstr!("u"), self.ar.get()) != 0,
152 "lua_getinfo failed with `u`"
153 );
154 #[cfg(feature = "luau")]
155 mlua_assert!(
156 ffi::lua_getinfo(self.lua.state, self.level, cstr!("a"), self.ar.get()) != 0,
157 "lua_getinfo failed with `a`"
158 );
159
160 #[cfg(not(feature = "luau"))]
161 let stack = DebugStack {
162 num_ups: (*self.ar.get()).nups as i32,
163 #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua-factorio"))]
164 num_params: (*self.ar.get()).nparams as i32,
165 #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua-factorio"))]
166 is_vararg: (*self.ar.get()).isvararg != 0,
167 };
168 #[cfg(feature = "luau")]
169 let stack = DebugStack {
170 num_ups: (*self.ar.get()).nupvals as i32,
171 num_params: (*self.ar.get()).nparams as i32,
172 is_vararg: (*self.ar.get()).isvararg != 0,
173 };
174 stack
175 }
176 }
177}
178
179enum ActivationRecord {
180 #[cfg(not(feature = "luau"))]
181 Borrowed(*mut lua_Debug),
182 Owned(UnsafeCell<lua_Debug>),
183}
184
185impl ActivationRecord {
186 #[inline]
187 fn get(&self) -> *mut lua_Debug {
188 match self {
189 #[cfg(not(feature = "luau"))]
190 ActivationRecord::Borrowed(x) => *x,
191 ActivationRecord::Owned(x) => x.get(),
192 }
193 }
194}
195
196#[derive(Clone, Copy, Debug, PartialEq, Eq)]
198pub enum DebugEvent {
199 Call,
200 Ret,
201 TailCall,
202 Line,
203 Count,
204 Unknown(c_int),
205}
206
207#[derive(Clone, Debug)]
208pub struct DebugNames<'a> {
209 pub name: Option<&'a [u8]>,
210 pub name_what: Option<&'a [u8]>,
211}
212
213#[derive(Clone, Debug)]
214pub struct DebugSource<'a> {
215 pub source: Option<&'a [u8]>,
216 pub short_src: Option<&'a [u8]>,
217 pub line_defined: i32,
218 #[cfg(not(feature = "luau"))]
219 pub last_line_defined: i32,
220 pub what: Option<&'a [u8]>,
221}
222
223#[derive(Copy, Clone, Debug)]
224pub struct DebugStack {
225 pub num_ups: i32,
226 #[cfg(any(
228 feature = "lua54",
229 feature = "lua53",
230 feature = "lua52",
231 feature = "luau",
232 feature = "lua-factorio"
233 ))]
234 pub num_params: i32,
235 #[cfg(any(
237 feature = "lua54",
238 feature = "lua53",
239 feature = "lua52",
240 feature = "luau",
241 feature = "lua-factorio"
242 ))]
243 pub is_vararg: bool,
244}
245
246#[cfg(not(feature = "luau"))]
248#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
249#[derive(Clone, Copy, Debug, Default)]
250pub struct HookTriggers {
251 pub on_calls: bool,
253 pub on_returns: bool,
255 pub every_line: bool,
257 pub every_nth_instruction: Option<u32>,
264}
265
266#[cfg(not(feature = "luau"))]
267impl HookTriggers {
268 pub fn on_calls() -> Self {
272 HookTriggers {
273 on_calls: true,
274 ..Default::default()
275 }
276 }
277
278 pub fn on_returns() -> Self {
282 HookTriggers {
283 on_returns: true,
284 ..Default::default()
285 }
286 }
287
288 pub fn every_line() -> Self {
292 HookTriggers {
293 every_line: true,
294 ..Default::default()
295 }
296 }
297
298 pub fn every_nth_instruction(n: u32) -> Self {
302 HookTriggers {
303 every_nth_instruction: Some(n),
304 ..Default::default()
305 }
306 }
307
308 pub(crate) fn mask(&self) -> c_int {
310 let mut mask: c_int = 0;
311 if self.on_calls {
312 mask |= ffi::LUA_MASKCALL
313 }
314 if self.on_returns {
315 mask |= ffi::LUA_MASKRET
316 }
317 if self.every_line {
318 mask |= ffi::LUA_MASKLINE
319 }
320 if self.every_nth_instruction.is_some() {
321 mask |= ffi::LUA_MASKCOUNT
322 }
323 mask
324 }
325
326 pub(crate) fn count(&self) -> c_int {
329 self.every_nth_instruction.unwrap_or(0) as c_int
330 }
331}
332
333#[cfg(not(feature = "luau"))]
334impl BitOr for HookTriggers {
335 type Output = Self;
336
337 fn bitor(mut self, rhs: Self) -> Self::Output {
338 self.on_calls |= rhs.on_calls;
339 self.on_returns |= rhs.on_returns;
340 self.every_line |= rhs.every_line;
341 if self.every_nth_instruction.is_none() && rhs.every_nth_instruction.is_some() {
342 self.every_nth_instruction = rhs.every_nth_instruction;
343 }
344 self
345 }
346}
347
348#[cfg(not(feature = "luau"))]
349impl BitOrAssign for HookTriggers {
350 fn bitor_assign(&mut self, rhs: Self) {
351 *self = *self | rhs;
352 }
353}