1use std::cell::RefCell;
13use std::io::{self, BufRead, Write};
14use std::rc::Rc;
15
16use lua_types::{GcRef, LuaError, LuaString, LuaType, LuaValue};
17use crate::state_stub::{LuaState, LuaStateStubExt as _, LuaDebug as DebugInfo};
18
19const HOOKKEY: &[u8] = b"_HOOKKEY";
24
25const HOOKNAMES: &[&[u8]; 5] = &[b"call", b"return", b"line", b"count", b"tail call"];
29
30const MASK_CALL: u32 = 1 << 0;
32const MASK_RET: u32 = 1 << 1;
33const MASK_LINE: u32 = 1 << 2;
34const MASK_COUNT: u32 = 1 << 3;
35
36pub(crate) type LibFn = fn(&mut LuaState) -> Result<usize, LuaError>;
40
41#[expect(dead_code, reason = "ported stdlib helper; not yet wired into the runtime")]
47pub(crate) type HookFn = fn(&mut LuaState, i32, i32) -> Result<(), LuaError>;
48
49type UpvalId = usize;
58
59#[derive(Clone)]
60enum DebugThreadTarget {
61 Current,
62 Other(Rc<RefCell<LuaState>>),
63 Unavailable,
64}
65
66fn resolve_debug_thread_target(
67 state: &LuaState,
68 target_thread: &Option<GcRef<lua_types::value::LuaThread>>,
69) -> DebugThreadTarget {
70 let Some(thread) = target_thread else {
71 return DebugThreadTarget::Current;
72 };
73
74 if thread.id == state.cached_thread_id {
75 return DebugThreadTarget::Current;
76 }
77
78 let g = state.global();
79 if thread.id == g.main_thread_id {
80 DebugThreadTarget::Unavailable
81 } else {
82 g.threads
83 .get(&thread.id)
84 .map(|entry| DebugThreadTarget::Other(entry.state.clone()))
85 .unwrap_or(DebugThreadTarget::Unavailable)
86 }
87}
88
89fn check_cross_thread_stack(
99 state: &mut LuaState,
100 target_is_self: bool,
101 n: i32,
102) -> Result<(), LuaError> {
103 if !target_is_self {
105 state.ensure_stack(n, "stack overflow")?;
109 }
110 Ok(())
111}
112
113fn getthread(state: &mut LuaState) -> (i32, Option<GcRef<lua_types::value::LuaThread>>) {
117 if state.type_at(1) == LuaType::Thread {
118 let thread = state.to_thread_at(1);
119 return (1, thread);
120 }
121 (0, None)
122}
123
124fn settabss(state: &mut LuaState, k: &[u8], v: Option<&[u8]>) -> Result<(), LuaError> {
130 match v {
131 Some(s) => {
132 let ls = state.intern_str(s)?;
133 state.push(LuaValue::Str(ls));
134 }
135 None => { state.push(LuaValue::Nil); }
136 }
137 state.set_field(-2, k)
138}
139
140fn settabsi(state: &mut LuaState, k: &[u8], v: i32) -> Result<(), LuaError> {
143 state.push(LuaValue::Int(v as i64));
144 state.set_field(-2, k)
145}
146
147fn settabsb(state: &mut LuaState, k: &[u8], v: bool) -> Result<(), LuaError> {
150 state.push(LuaValue::Bool(v));
151 state.set_field(-2, k)
152}
153
154fn treat_stack_option(
161 state: &mut LuaState,
162 target_is_self: bool,
163 fname: &[u8],
164) -> Result<(), LuaError> {
165 if target_is_self {
166 state.rotate(-2, 1)?;
167 } else {
168 state.push(LuaValue::Nil);
172 }
173 state.set_field(-2, fname)
174}
175
176fn move_stack_option_from_target(
177 state: &mut LuaState,
178 target: &mut LuaState,
179 fname: &[u8],
180) -> Result<(), LuaError> {
181 let val = target.get_at(target.top_idx() - 1);
182 target.pop_n(1);
183 state.push(val);
184 state.set_field(-2, fname)
185}
186
187pub(crate) fn get_registry(state: &mut LuaState) -> Result<usize, LuaError> {
192 state.push_registry()?;
193 Ok(1)
194}
195
196pub(crate) fn get_metatable(state: &mut LuaState) -> Result<usize, LuaError> {
199 state.check_arg_any(1)?;
200 if !state.get_metatable(1)? {
201 state.push(LuaValue::Nil);
202 }
203 Ok(1)
204}
205
206pub(crate) fn set_metatable(state: &mut LuaState) -> Result<usize, LuaError> {
210 let t = state.type_at(2);
211 if !(t == LuaType::Nil || t == LuaType::Table) {
212 let got = state.arg(2);
213 return Err(LuaError::type_arg_error(2, "nil or table", &got));
214 }
215 lua_vm::api::set_top(state, 2)?;
216 state.set_metatable(1)?;
217 Ok(1)
218}
219
220pub(crate) fn get_uservalue(state: &mut LuaState) -> Result<usize, LuaError> {
225 let n = state.opt_arg_integer(2, 1)? as i32;
226 if state.type_at(1) != LuaType::UserData {
227 state.push_fail()?;
228 return Ok(1);
229 }
230 let ty = state.get_iuservalue(1, n)?;
231 if ty != LuaType::None {
232 state.push(LuaValue::Bool(true));
233 return Ok(2);
234 }
235 Ok(1)
236}
237
238pub(crate) fn set_uservalue(state: &mut LuaState) -> Result<usize, LuaError> {
242 let n = state.opt_arg_integer(3, 1)? as i32;
243 state.check_arg_type(1, LuaType::UserData)?;
244 state.check_arg_any(2)?;
245 lua_vm::api::set_top(state, 2)?;
246 if !state.set_iuservalue(1, n)? {
247 state.push_fail()?;
248 }
249 Ok(1)
250}
251
252pub(crate) fn get_info(state: &mut LuaState) -> Result<usize, LuaError> {
257 let mut ar = DebugInfo::default();
258
259 let (arg, other_thread) = getthread(state);
260 let target_is_self = other_thread.is_none();
261 let target_state = resolve_debug_thread_target(state, &other_thread);
262
263 let raw_opts: Vec<u8> = state.opt_arg_string(arg + 2, b"flnSrtu")?.to_vec();
265
266 check_cross_thread_stack(state, target_is_self, 3)?;
267
268 if raw_opts.first() == Some(&b'>') {
269 return Err(LuaError::arg_error(arg + 2, "invalid option '>'"));
270 }
271
272 let options: Vec<u8>;
274 let info_target_owner: Option<Rc<RefCell<LuaState>>>;
275 let mut info_target: Option<std::cell::RefMut<'_, LuaState>> = None;
276 let mut info_target_is_self = target_is_self;
277
278 if state.type_at(arg + 1) == LuaType::Function {
279 let mut prefixed = Vec::with_capacity(raw_opts.len() + 1);
281 prefixed.push(b'>');
282 prefixed.extend_from_slice(&raw_opts);
283 options = prefixed;
284
285 if target_is_self {
286 state.push_value_at(arg + 1)?;
287 } else {
288 }
292
293 if state.get_debug_info(&options, &mut ar).is_err() {
295 return Err(LuaError::arg_error(arg + 2, "invalid option"));
296 }
297 } else {
298 options = raw_opts;
299
300 let level = state.check_arg_integer(arg + 1)? as i32;
301 match target_state {
302 DebugThreadTarget::Current | DebugThreadTarget::Unavailable => {
303 info_target_is_self = true;
304 if !state.get_stack_level(level, &mut ar) {
305 state.push_fail()?;
306 return Ok(1);
307 }
308
309 if state.get_debug_info(&options, &mut ar).is_err() {
310 return Err(LuaError::arg_error(arg + 2, "invalid option"));
311 }
312 }
313 DebugThreadTarget::Other(target_state) => {
314 info_target_owner = Some(target_state);
315 let mut target = info_target_owner
316 .as_ref()
317 .expect("target owner just stored")
318 .borrow_mut();
319 if !target.get_stack_level(level, &mut ar) {
320 state.push_fail()?;
321 return Ok(1);
322 }
323 if target.get_debug_info(&options, &mut ar).is_err() {
324 return Err(LuaError::arg_error(arg + 2, "invalid option"));
325 }
326 info_target = Some(target);
327 }
328 }
329 }
330
331 let result_tbl = state.new_table();
332 state.push(LuaValue::Table(result_tbl));
333
334 if options.contains(&b'S') {
335 let src = state.intern_str(ar.source_bytes())?;
336 state.push(LuaValue::Str(src));
337 state.set_field(-2, b"source")?;
338
339 settabss(state, b"short_src", Some(ar.short_src_bytes()))?;
340 settabsi(state, b"linedefined", ar.linedefined)?;
341 settabsi(state, b"lastlinedefined", ar.lastlinedefined)?;
342 settabss(state, b"what", Some(ar.what_bytes()))?;
343 }
344 if options.contains(&b'l') {
345 settabsi(state, b"currentline", ar.currentline)?;
346 }
347 if options.contains(&b'u') {
348 settabsi(state, b"nups", ar.nups as i32)?;
349 settabsi(state, b"nparams", ar.nparams as i32)?;
350 settabsb(state, b"isvararg", ar.isvararg)?;
351 }
352 if options.contains(&b'n') {
353 let name_opt: Option<&[u8]> = ar.name.as_deref();
354 settabss(state, b"name", name_opt)?;
355 settabss(state, b"namewhat", Some(ar.namewhat_bytes()))?;
356 }
357 if options.contains(&b'r') {
358 settabsi(state, b"ftransfer", ar.ftransfer as i32)?;
359 settabsi(state, b"ntransfer", ar.ntransfer as i32)?;
360 }
361 if options.contains(&b't') {
362 settabsb(state, b"istailcall", ar.istailcall)?;
363 }
364 if options.contains(&b'L') {
370 if info_target_is_self {
371 treat_stack_option(state, true, b"activelines")?;
372 } else if let Some(target) = info_target.as_mut() {
373 move_stack_option_from_target(state, &mut **target, b"activelines")?;
374 } else {
375 state.push(LuaValue::Nil);
376 state.set_field(-2, b"activelines")?;
377 }
378 }
379 if options.contains(&b'f') {
380 if info_target_is_self {
381 treat_stack_option(state, true, b"func")?;
382 } else if let Some(target) = info_target.as_mut() {
383 move_stack_option_from_target(state, &mut **target, b"func")?;
384 } else {
385 state.push(LuaValue::Nil);
386 state.set_field(-2, b"func")?;
387 }
388 }
389
390 Ok(1)
391}
392
393pub(crate) fn get_local(state: &mut LuaState) -> Result<usize, LuaError> {
400 let (arg, other_thread) = getthread(state);
401 let target_state = resolve_debug_thread_target(state, &other_thread);
402
403 let nvar = state.check_arg_integer(arg + 2)? as i32;
404
405 if state.type_at(arg + 1) == LuaType::Function {
406 state.push_value_at(arg + 1)?;
407 let name = state.get_param_name(0, nvar)?;
410 match name {
411 Some(n) => {
412 let ls = state.intern_str(&n)?;
413 state.push(LuaValue::Str(ls));
414 }
415 None => { state.push(LuaValue::Nil); }
416 }
417 return Ok(1);
420 }
421
422 let level = state.check_arg_integer(arg + 1)? as i32;
424 let mut ar = DebugInfo::default();
425
426 let name = match target_state {
427 DebugThreadTarget::Current | DebugThreadTarget::Unavailable => {
428 if !state.get_stack_level(level, &mut ar) {
429 return Err(LuaError::arg_error(arg + 1, "level out of range"));
430 }
431 check_cross_thread_stack(state, true, 1)?;
432 state.get_local_at(&ar, nvar)?
434 }
435 DebugThreadTarget::Other(target_state) => {
436 let mut target = target_state.borrow_mut();
437 if !target.get_stack_level(level, &mut ar) {
438 return Err(LuaError::arg_error(arg + 1, "level out of range"));
439 }
440 check_cross_thread_stack(state, false, 1)?;
441 let name = target.get_local_at(&ar, nvar)?;
442 if name.is_some() {
443 let val = target.get_at(target.top_idx() - 1);
444 target.pop_n(1);
445 state.push(val);
446 }
447 name
448 }
449 };
450
451 if let Some(n) = name {
452 let ls = state.intern_str(&n)?;
453 state.push(LuaValue::Str(ls));
454 state.rotate(-2, 1)?;
455 Ok(2)
456 } else {
457 state.push_fail()?;
458 Ok(1)
459 }
460}
461
462pub(crate) fn set_local(state: &mut LuaState) -> Result<usize, LuaError> {
467 let (arg, other_thread) = getthread(state);
468 let target_state = resolve_debug_thread_target(state, &other_thread);
469
470 let level = state.check_arg_integer(arg + 1)? as i32;
471 let nvar = state.check_arg_integer(arg + 2)? as i32;
472
473 let mut ar = DebugInfo::default();
474
475 state.check_arg_any(arg + 3)?;
476 lua_vm::api::set_top(state, arg + 3)?;
477
478 let name = match target_state {
479 DebugThreadTarget::Current | DebugThreadTarget::Unavailable => {
480 if !state.get_stack_level(level, &mut ar) {
481 return Err(LuaError::arg_error(arg + 1, "level out of range"));
482 }
483 check_cross_thread_stack(state, true, 1)?;
484 let name = state.set_local_at(&ar, nvar)?;
485 if name.is_none() {
486 state.pop_n(1);
487 }
488 name
489 }
490 DebugThreadTarget::Other(target_state) => {
491 let new_val = state.get_at(state.top_idx() - 1);
492 let mut target = target_state.borrow_mut();
493 if !target.get_stack_level(level, &mut ar) {
494 return Err(LuaError::arg_error(arg + 1, "level out of range"));
495 }
496 check_cross_thread_stack(state, false, 1)?;
497 target.push(new_val);
498 let name = target.set_local_at(&ar, nvar)?;
499 if name.is_none() {
500 target.pop_n(1);
501 }
502 state.pop_n(1);
503 name
504 }
505 };
506
507 match name {
508 Some(n) => {
509 let ls = state.intern_str(&n)?;
510 state.push(LuaValue::Str(ls));
511 }
512 None => { state.push(LuaValue::Nil); }
513 }
514 Ok(1)
515}
516
517fn aux_upvalue(state: &mut LuaState, get: bool) -> Result<usize, LuaError> {
528 let n = state.check_arg_integer(2)? as i32;
529 state.check_arg_type(1, LuaType::Function)?;
530
531 let name: Option<Vec<u8>> = if get {
532 state.get_upvalue(1, n)?
534 } else {
535 state.set_upvalue(1, n)?
537 };
538
539 let name_ref = match name {
540 Some(n) => n,
541 None => return Ok(0),
542 };
543
544 let ls = state.intern_str(&name_ref)?;
545 state.push(LuaValue::Str(ls));
546
547 if get {
550 state.insert(-2)?;
551 }
552
553 Ok(if get { 2 } else { 1 })
554}
555
556pub(crate) fn get_upvalue(state: &mut LuaState) -> Result<usize, LuaError> {
559 aux_upvalue(state, true)
560}
561
562pub(crate) fn set_upvalue(state: &mut LuaState) -> Result<usize, LuaError> {
566 state.check_arg_any(3)?;
567 aux_upvalue(state, false)
568}
569
570fn check_upval(
575 state: &mut LuaState,
576 argf: i32,
577 argnup: i32,
578 require_valid: bool,
579) -> Result<(Option<UpvalId>, i32), LuaError> {
580 let nup = state.check_arg_integer(argnup)? as i32;
581 state.check_arg_type(argf, LuaType::Function)?;
582 let id: Option<UpvalId> = match state.upvalue_id(argf, nup) {
587 Ok(p) if p.is_null() => None,
588 Ok(p) => Some(p as usize),
589 Err(_) => None,
590 };
591 if require_valid && id.is_none() {
592 return Err(LuaError::arg_error(argnup, "invalid upvalue index"));
593 }
594 Ok((id, nup))
595}
596
597pub(crate) fn upvalue_id(state: &mut LuaState) -> Result<usize, LuaError> {
601 let (id, _nup) = check_upval(state, 1, 2, false)?;
602 match id {
603 Some(uid) => {
604 lua_vm::api::push_light_userdata(state, uid as *mut core::ffi::c_void);
605 }
606 None => {
607 state.push_fail()?;
608 }
609 }
610 Ok(1)
611}
612
613pub(crate) fn upvalue_join(state: &mut LuaState) -> Result<usize, LuaError> {
617 let (_id1, n1) = check_upval(state, 1, 2, true)?;
618 let (_id2, n2) = check_upval(state, 3, 4, true)?;
619 if state.is_c_function_at(1) {
620 return Err(LuaError::arg_error(1, "Lua function expected"));
621 }
622 if state.is_c_function_at(3) {
623 return Err(LuaError::arg_error(3, "Lua function expected"));
624 }
625 state.join_upvalues(1, n1, 3, n2)?;
626 Ok(0)
627}
628
629pub(crate) fn hookf(state: &mut LuaState, event: i32, currentline: i32) -> Result<(), LuaError> {
635 state.get_registry_field(HOOKKEY)?;
636 state.push_thread()?;
637 if state.raw_get(-2)? == LuaType::Function {
638 let event_idx = event.clamp(0, HOOKNAMES.len() as i32 - 1) as usize;
639 let event_str = state.intern_str(HOOKNAMES[event_idx])?;
640 state.push(LuaValue::Str(event_str));
641
642 if currentline >= 0 {
643 state.push(LuaValue::Int(currentline as i64));
644 } else {
645 state.push(LuaValue::Nil);
646 }
647
648 state.call(2, 0)?;
649 }
650 Ok(())
653}
654
655fn make_mask(smask: &[u8], count: i32) -> u32 {
659 let mut mask: u32 = 0;
660 if smask.contains(&b'c') {
661 mask |= MASK_CALL;
662 }
663 if smask.contains(&b'r') {
664 mask |= MASK_RET;
665 }
666 if smask.contains(&b'l') {
667 mask |= MASK_LINE;
668 }
669 if count > 0 {
670 mask |= MASK_COUNT;
671 }
672 mask
673}
674
675fn unmake_mask(mask: u32) -> Vec<u8> {
679 let mut smask = Vec::with_capacity(3);
680 if mask & MASK_CALL != 0 {
681 smask.push(b'c');
682 }
683 if mask & MASK_RET != 0 {
684 smask.push(b'r');
685 }
686 if mask & MASK_LINE != 0 {
687 smask.push(b'l');
688 }
689 smask
690}
691
692pub(crate) fn set_hook(state: &mut LuaState) -> Result<usize, LuaError> {
696 let (arg, other_thread) = getthread(state);
697 let target_is_self = other_thread.is_none();
698
699 let hook_active: bool;
700 let mask: u32;
701 let count: i32;
702
703 if matches!(state.type_at(arg + 1), LuaType::None | LuaType::Nil) {
704 lua_vm::api::set_top(state, arg + 1)?;
705 hook_active = false;
706 mask = 0;
707 count = 0;
708 } else {
709 let smask: Vec<u8> = state.check_arg_string(arg + 2)?.to_vec();
710 state.check_arg_type(arg + 1, LuaType::Function)?;
711 count = state.opt_arg_integer(arg + 3, 0)? as i32;
712 hook_active = true;
713 mask = make_mask(&smask, count);
714 }
715
716 if !state.get_or_create_registry_subtable(HOOKKEY)? {
717 let k = state.intern_str(b"k")?;
720 state.push(LuaValue::Str(k));
721 state.set_field(-2, b"__mode")?;
722 state.push_value_at(-1)?;
723 state.set_metatable(-2)?;
724 }
725
726 check_cross_thread_stack(state, target_is_self, 1)?;
727 let target_state = resolve_debug_thread_target(state, &other_thread);
728 match &target_state {
729 DebugThreadTarget::Other(st) => {
730 st.borrow_mut().ensure_stack(1, "stack overflow")?;
731 }
732 DebugThreadTarget::Current => {}
733 DebugThreadTarget::Unavailable => {}
734 }
735
736 if target_is_self {
737 state.push_thread()?;
738 } else {
739 let thr = other_thread.clone().expect("other_thread is Some when target_is_self is false");
746 state.push(lua_types::value::LuaValue::Thread(thr));
747 }
748 state.push_value_at(arg + 1)?;
749 state.raw_set(-3)?;
750
751 let hook_box: Option<Box<dyn FnMut(&mut LuaState, &lua_vm::debug::LuaDebug)>> = if hook_active {
752 Some(Box::new(|st, ar| {
753 let _ = hookf(st, ar.event, ar.currentline);
754 }))
755 } else {
756 None
757 };
758 match target_state {
759 DebugThreadTarget::Current => {
760 lua_vm::debug::set_hook(state, hook_box, mask as i32, count);
761 }
762 DebugThreadTarget::Other(target_state) => {
763 lua_vm::debug::set_hook(&mut target_state.borrow_mut(), hook_box, mask as i32, count);
764 }
765 DebugThreadTarget::Unavailable => {
766 return Ok(0);
770 }
771 }
772
773 Ok(0)
774}
775
776pub(crate) fn get_hook(state: &mut LuaState) -> Result<usize, LuaError> {
780 let (_arg, other_thread) = getthread(state);
781 let target_is_self = other_thread.is_none();
782 let target_state = resolve_debug_thread_target(state, &other_thread);
783
784 let (mask, hook_is_set, hook_is_internal, hook_count) = match target_state {
785 DebugThreadTarget::Current => {
786 (
787 state.get_hook_mask(),
788 state.hook_is_set(),
789 state.hook_is_internal_lua_hook(),
790 state.get_hook_count(),
791 )
792 }
793 DebugThreadTarget::Other(target_state) => {
794 let mut target_state = target_state.borrow_mut();
795 (
796 target_state.get_hook_mask(),
797 target_state.hook_is_set(),
798 target_state.hook_is_internal_lua_hook(),
799 target_state.get_hook_count(),
800 )
801 }
802 DebugThreadTarget::Unavailable => (0u32, false, false, 0i32),
803 };
804
805 if !hook_is_set {
806 state.push_fail()?;
807 return Ok(1);
808 }
809
810 if !hook_is_internal {
812 let s = state.intern_str(b"external hook")?;
813 state.push(LuaValue::Str(s));
814 } else {
815 state.get_registry_field(HOOKKEY)?;
816 check_cross_thread_stack(state, target_is_self, 1)?;
817 if target_is_self {
818 state.push_thread()?;
819 } else {
820 let key_thread = other_thread
821 .expect("other_thread is Some when target_is_self is false")
822 .clone();
823 state.push(lua_types::value::LuaValue::Thread(key_thread));
824 }
825 state.raw_get(-2)?;
826 state.remove(-2)?;
827 }
828
829 let smask = unmake_mask(mask);
830 let ls = state.intern_str(&smask)?;
831 state.push(LuaValue::Str(ls));
832
833 state.push(LuaValue::Int(hook_count as i64));
834
835 Ok(3)
836}
837
838pub(crate) fn debug_interactive(state: &mut LuaState) -> Result<usize, LuaError> {
845 let stdin = io::stdin();
846 loop {
847 eprint!("lua_debug> ");
848 let _ = io::stderr().flush();
849
850 let mut line = String::new();
855 let n = stdin
856 .lock()
857 .read_line(&mut line)
858 .map_err(|e| LuaError::runtime(format_args!("stdin read error: {}", e)))?;
859
860 if n == 0 || line == "cont\n" {
861 return Ok(0);
862 }
863
864 let bytes: &[u8] = line.as_bytes();
865
866 let result = state
869 .load_buffer(bytes, b"=(debug command)", None)
870 .and_then(|_| state.protected_call(0, 0, 0));
871
872 if let Err(_) = result {
873 eprintln!("(error in debug command)");
878 state.pop_n(1);
879 }
880
881 lua_vm::api::set_top(state, 0)?;
882 }
883}
884
885pub(crate) fn traceback(state: &mut LuaState) -> Result<usize, LuaError> {
892 let (arg, other_thread) = getthread(state);
893 let target_is_self = other_thread.is_none();
894
895 let msg_owned: Option<Vec<u8>> = state
897 .to_lua_string(arg + 1)
898 .map(|s: GcRef<LuaString>| s.as_bytes().to_vec());
899
900 let arg1_ty = state.type_at(arg + 1);
901 if msg_owned.is_none() && !matches!(arg1_ty, LuaType::None | LuaType::Nil) {
902 state.push_value_at(arg + 1)?;
903 } else {
904 let default_level: i64 = if target_is_self { 1 } else { 0 };
905 let level = state.opt_arg_integer(arg + 2, default_level)? as i32;
906
907 match resolve_debug_thread_target(state, &other_thread) {
908 DebugThreadTarget::Current => {
909 crate::auxlib::traceback(state, None, msg_owned.as_deref(), level)?;
910 }
911 DebugThreadTarget::Other(target_state) => {
912 let mut target_state = target_state.borrow_mut();
913 crate::auxlib::traceback(
914 state,
915 Some(&mut *target_state),
916 msg_owned.as_deref(),
917 level,
918 )?;
919 }
920 DebugThreadTarget::Unavailable => {
921 crate::auxlib::traceback(state, None, msg_owned.as_deref(), level)?;
922 }
923 }
924 }
925 Ok(1)
926}
927
928pub(crate) fn set_c_stack_limit(state: &mut LuaState) -> Result<usize, LuaError> {
932 let limit = state.check_arg_integer(1)? as i32;
933 let res = state.set_c_stack_limit(limit)?;
934 state.push(LuaValue::Int(res as i64));
935 Ok(1)
936}
937
938pub(crate) const DBLIB: &[(&[u8], LibFn)] = &[
943 (b"debug", debug_interactive as LibFn),
944 (b"getuservalue", get_uservalue as LibFn),
945 (b"gethook", get_hook as LibFn),
946 (b"getinfo", get_info as LibFn),
947 (b"getlocal", get_local as LibFn),
948 (b"getregistry", get_registry as LibFn),
949 (b"getmetatable", get_metatable as LibFn),
950 (b"getupvalue", get_upvalue as LibFn),
951 (b"upvaluejoin", upvalue_join as LibFn),
952 (b"upvalueid", upvalue_id as LibFn),
953 (b"setuservalue", set_uservalue as LibFn),
954 (b"sethook", set_hook as LibFn),
955 (b"setlocal", set_local as LibFn),
956 (b"setmetatable", set_metatable as LibFn),
957 (b"setupvalue", set_upvalue as LibFn),
958 (b"traceback", traceback as LibFn),
959 (b"setcstacklimit", set_c_stack_limit as LibFn),
960];
961
962pub fn open_debug(state: &mut LuaState) -> Result<usize, LuaError> {
966 state.new_lib(DBLIB)?;
967 Ok(1)
968}
969
970