1use std::cell::RefCell;
13#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
14use std::io::{self, BufRead, Write};
15use std::rc::Rc;
16
17use crate::state_stub::{LuaDebug as DebugInfo, LuaState, LuaStateStubExt as _};
18use lua_types::{GcRef, LuaError, LuaString, LuaType, LuaValue, LuaVersion};
19
20const HOOKKEY: &[u8] = b"_HOOKKEY";
25
26const HOOKNAMES: &[&[u8]; 5] = &[b"call", b"return", b"line", b"count", b"tail call"];
30
31const MASK_CALL: u32 = 1 << 0;
33const MASK_RET: u32 = 1 << 1;
34const MASK_LINE: u32 = 1 << 2;
35const MASK_COUNT: u32 = 1 << 3;
36
37pub(crate) type LibFn = fn(&mut LuaState) -> Result<usize, LuaError>;
41
42#[expect(
48 dead_code,
49 reason = "ported stdlib helper; not yet wired into the runtime"
50)]
51pub(crate) type HookFn = fn(&mut LuaState, i32, i32) -> Result<(), LuaError>;
52
53type UpvalId = usize;
62
63#[derive(Clone)]
64enum DebugThreadTarget {
65 Current,
66 Other(Rc<RefCell<LuaState>>),
67 Unavailable,
68}
69
70fn resolve_debug_thread_target(
71 state: &LuaState,
72 target_thread: &Option<GcRef<lua_types::value::LuaThread>>,
73) -> DebugThreadTarget {
74 let Some(thread) = target_thread else {
75 return DebugThreadTarget::Current;
76 };
77
78 if thread.id == state.cached_thread_id {
79 return DebugThreadTarget::Current;
80 }
81
82 let g = state.global();
83 if thread.id == g.main_thread_id {
84 DebugThreadTarget::Unavailable
85 } else {
86 g.threads
87 .get(&thread.id)
88 .map(|entry| DebugThreadTarget::Other(entry.state.clone()))
89 .unwrap_or(DebugThreadTarget::Unavailable)
90 }
91}
92
93fn check_cross_thread_stack(
103 state: &mut LuaState,
104 target_is_self: bool,
105 n: i32,
106) -> Result<(), LuaError> {
107 if !target_is_self {
109 state.ensure_stack(n, "stack overflow")?;
113 }
114 Ok(())
115}
116
117fn getthread(state: &mut LuaState) -> (i32, Option<GcRef<lua_types::value::LuaThread>>) {
121 if state.type_at(1) == LuaType::Thread {
122 let thread = state.to_thread_at(1);
123 return (1, thread);
124 }
125 (0, None)
126}
127
128fn settabss(state: &mut LuaState, k: &[u8], v: Option<&[u8]>) -> Result<(), LuaError> {
134 match v {
135 Some(s) => {
136 let ls = state.intern_str(s)?;
137 state.push(LuaValue::Str(ls));
138 }
139 None => {
140 state.push(LuaValue::Nil);
141 }
142 }
143 state.set_field(-2, k)
144}
145
146fn settabsi(state: &mut LuaState, k: &[u8], v: i32) -> Result<(), LuaError> {
149 state.push(LuaValue::Int(v as i64));
150 state.set_field(-2, k)
151}
152
153fn settabsb(state: &mut LuaState, k: &[u8], v: bool) -> Result<(), LuaError> {
156 state.push(LuaValue::Bool(v));
157 state.set_field(-2, k)
158}
159
160fn treat_stack_option(
167 state: &mut LuaState,
168 target_is_self: bool,
169 fname: &[u8],
170) -> Result<(), LuaError> {
171 if target_is_self {
172 state.rotate(-2, 1)?;
173 } else {
174 state.push(LuaValue::Nil);
178 }
179 state.set_field(-2, fname)
180}
181
182fn move_stack_option_from_target(
183 state: &mut LuaState,
184 target: &mut LuaState,
185 fname: &[u8],
186) -> Result<(), LuaError> {
187 let val = target.get_at(target.top_idx() - 1);
188 target.pop_n(1);
189 state.push(val);
190 state.set_field(-2, fname)
191}
192
193pub(crate) fn get_registry(state: &mut LuaState) -> Result<usize, LuaError> {
198 state.push_registry()?;
199 Ok(1)
200}
201
202pub(crate) fn get_metatable(state: &mut LuaState) -> Result<usize, LuaError> {
205 state.check_arg_any(1)?;
206 if !state.get_metatable(1)? {
207 state.push(LuaValue::Nil);
208 }
209 Ok(1)
210}
211
212pub(crate) fn set_metatable(state: &mut LuaState) -> Result<usize, LuaError> {
216 let t = state.type_at(2);
217 if !(t == LuaType::Nil || t == LuaType::Table) {
218 let got = state.arg(2);
219 return Err(LuaError::type_arg_error(2, "nil or table", &got));
220 }
221 lua_vm::api::set_top(state, 2)?;
222 state.set_metatable(1)?;
223 Ok(1)
224}
225
226pub(crate) fn get_uservalue(state: &mut LuaState) -> Result<usize, LuaError> {
231 let n = state.opt_arg_integer(2, 1)? as i32;
232 if state.type_at(1) != LuaType::UserData {
233 state.push_fail()?;
234 return Ok(1);
235 }
236 let ty = state.get_iuservalue(1, n)?;
237 if ty != LuaType::None {
238 state.push(LuaValue::Bool(true));
239 return Ok(2);
240 }
241 Ok(1)
242}
243
244pub(crate) fn set_uservalue(state: &mut LuaState) -> Result<usize, LuaError> {
248 let n = state.opt_arg_integer(3, 1)? as i32;
249 state.check_arg_type(1, LuaType::UserData)?;
250 state.check_arg_any(2)?;
251 lua_vm::api::set_top(state, 2)?;
252 if !state.set_iuservalue(1, n)? {
253 state.push_fail()?;
254 }
255 Ok(1)
256}
257
258pub(crate) fn get_info(state: &mut LuaState) -> Result<usize, LuaError> {
263 let mut ar = DebugInfo::default();
264
265 let (arg, other_thread) = getthread(state);
266 let target_is_self = other_thread.is_none();
267 let target_state = resolve_debug_thread_target(state, &other_thread);
268
269 let raw_opts: Vec<u8> = state.opt_arg_string(arg + 2, b"flnSrtu")?.to_vec();
271
272 check_cross_thread_stack(state, target_is_self, 3)?;
273
274 if raw_opts.first() == Some(&b'>') {
275 return Err(lua_vm::debug::arg_error_impl(
276 state,
277 arg + 2,
278 b"invalid option '>'",
279 ));
280 }
281
282 let options: Vec<u8>;
284 let info_target_owner: Option<Rc<RefCell<LuaState>>>;
285 let mut info_target: Option<crate::coro_lib::RootedThreadBorrow<'_>> = None;
286 let mut info_target_is_self = target_is_self;
287
288 if state.type_at(arg + 1) == LuaType::Function {
289 let mut prefixed = Vec::with_capacity(raw_opts.len() + 1);
291 prefixed.push(b'>');
292 prefixed.extend_from_slice(&raw_opts);
293 options = prefixed;
294
295 if target_is_self {
296 state.push_value_at(arg + 1)?;
297 } else {
298 }
302
303 if state.get_debug_info(&options, &mut ar).is_err() {
305 return Err(lua_vm::debug::arg_error_impl(
306 state,
307 arg + 2,
308 b"invalid option",
309 ));
310 }
311 } else {
312 options = raw_opts;
313
314 let level = state.check_arg_integer(arg + 1)? as i32;
315 match target_state {
316 DebugThreadTarget::Current | DebugThreadTarget::Unavailable => {
317 info_target_is_self = true;
318 if !state.get_stack_level(level, &mut ar) {
319 state.push_fail()?;
320 return Ok(1);
321 }
322
323 if state.get_debug_info(&options, &mut ar).is_err() {
324 return Err(lua_vm::debug::arg_error_impl(
325 state,
326 arg + 2,
327 b"invalid option",
328 ));
329 }
330 }
331 DebugThreadTarget::Other(target_state) => {
332 info_target_owner = Some(target_state);
333 let mut target = crate::coro_lib::borrow_thread_rooted(
334 state,
335 info_target_owner
336 .as_ref()
337 .expect("target owner just stored"),
338 );
339 if !target.get_stack_level(level, &mut ar) {
340 state.push_fail()?;
341 return Ok(1);
342 }
343 if target.get_debug_info(&options, &mut ar).is_err() {
344 return Err(lua_vm::debug::arg_error_impl(
345 state,
346 arg + 2,
347 b"invalid option",
348 ));
349 }
350 target.resnapshot();
351 info_target = Some(target);
352 }
353 }
354 }
355
356 let result_tbl = state.new_table();
357 state.push(LuaValue::Table(result_tbl));
358
359 if options.contains(&b'S') {
360 let src = state.intern_str(ar.source_bytes())?;
361 state.push(LuaValue::Str(src));
362 state.set_field(-2, b"source")?;
363
364 settabss(state, b"short_src", Some(ar.short_src_bytes()))?;
365 settabsi(state, b"linedefined", ar.linedefined)?;
366 settabsi(state, b"lastlinedefined", ar.lastlinedefined)?;
367 settabss(state, b"what", Some(ar.what_bytes()))?;
368 }
369 if options.contains(&b'l') {
370 settabsi(state, b"currentline", ar.currentline)?;
371 }
372 if options.contains(&b'u') {
373 settabsi(state, b"nups", ar.nups as i32)?;
374 settabsi(state, b"nparams", ar.nparams as i32)?;
375 settabsb(state, b"isvararg", ar.isvararg)?;
376 }
377 if options.contains(&b'n') {
378 let name_opt: Option<&[u8]> = ar.name.as_deref();
379 settabss(state, b"name", name_opt)?;
380 settabss(state, b"namewhat", Some(ar.namewhat_bytes()))?;
381 }
382 if options.contains(&b'r') {
383 settabsi(state, b"ftransfer", ar.ftransfer as i32)?;
384 settabsi(state, b"ntransfer", ar.ntransfer as i32)?;
385 }
386 if options.contains(&b't') {
387 settabsb(state, b"istailcall", ar.istailcall)?;
388 if matches!(state.global().lua_version, LuaVersion::V55) {
389 settabsi(state, b"extraargs", ar.extraargs as i32)?;
390 }
391 }
392 if options.contains(&b'L') {
398 if info_target_is_self {
399 treat_stack_option(state, true, b"activelines")?;
400 } else if let Some(target) = info_target.as_mut() {
401 move_stack_option_from_target(state, &mut **target, b"activelines")?;
402 } else {
403 state.push(LuaValue::Nil);
404 state.set_field(-2, b"activelines")?;
405 }
406 }
407 if options.contains(&b'f') {
408 if info_target_is_self {
409 treat_stack_option(state, true, b"func")?;
410 } else if let Some(target) = info_target.as_mut() {
411 move_stack_option_from_target(state, &mut **target, b"func")?;
412 } else {
413 state.push(LuaValue::Nil);
414 state.set_field(-2, b"func")?;
415 }
416 }
417
418 Ok(1)
419}
420
421pub(crate) fn get_local(state: &mut LuaState) -> Result<usize, LuaError> {
428 let (arg, other_thread) = getthread(state);
429 let target_state = resolve_debug_thread_target(state, &other_thread);
430
431 let nvar = state.check_arg_integer(arg + 2)? as i32;
432
433 if state.type_at(arg + 1) == LuaType::Function {
434 state.push_value_at(arg + 1)?;
435 let name = state.get_param_name(0, nvar)?;
438 match name {
439 Some(n) => {
440 let ls = state.intern_str(&n)?;
441 state.push(LuaValue::Str(ls));
442 }
443 None => {
444 state.push(LuaValue::Nil);
445 }
446 }
447 return Ok(1);
450 }
451
452 let level = state.check_arg_integer(arg + 1)? as i32;
454 let mut ar = DebugInfo::default();
455
456 let name = match target_state {
457 DebugThreadTarget::Current | DebugThreadTarget::Unavailable => {
458 if !state.get_stack_level(level, &mut ar) {
459 return Err(lua_vm::debug::arg_error_impl(
460 state,
461 arg + 1,
462 b"level out of range",
463 ));
464 }
465 check_cross_thread_stack(state, true, 1)?;
466 state.get_local_at(&ar, nvar)?
468 }
469 DebugThreadTarget::Other(target_state) => {
470 let mut target = crate::coro_lib::borrow_thread_rooted(state, &target_state);
471 if !target.get_stack_level(level, &mut ar) {
472 return Err(lua_vm::debug::arg_error_impl(
473 state,
474 arg + 1,
475 b"level out of range",
476 ));
477 }
478 check_cross_thread_stack(state, false, 1)?;
479 let name = target.get_local_at(&ar, nvar)?;
480 if name.is_some() {
481 let val = target.get_at(target.top_idx() - 1);
482 target.pop_n(1);
483 state.push(val);
484 }
485 name
486 }
487 };
488
489 if let Some(n) = name {
490 let ls = state.intern_str(&n)?;
491 state.push(LuaValue::Str(ls));
492 state.rotate(-2, 1)?;
493 Ok(2)
494 } else {
495 state.push_fail()?;
496 Ok(1)
497 }
498}
499
500pub(crate) fn set_local(state: &mut LuaState) -> Result<usize, LuaError> {
505 let (arg, other_thread) = getthread(state);
506 let target_state = resolve_debug_thread_target(state, &other_thread);
507
508 let level = state.check_arg_integer(arg + 1)? as i32;
509 let nvar = state.check_arg_integer(arg + 2)? as i32;
510
511 let mut ar = DebugInfo::default();
512
513 state.check_arg_any(arg + 3)?;
514 lua_vm::api::set_top(state, arg + 3)?;
515
516 let name = match target_state {
517 DebugThreadTarget::Current | DebugThreadTarget::Unavailable => {
518 if !state.get_stack_level(level, &mut ar) {
519 return Err(lua_vm::debug::arg_error_impl(
520 state,
521 arg + 1,
522 b"level out of range",
523 ));
524 }
525 check_cross_thread_stack(state, true, 1)?;
526 let name = state.set_local_at(&ar, nvar)?;
527 if name.is_none() {
528 state.pop_n(1);
529 }
530 name
531 }
532 DebugThreadTarget::Other(target_state) => {
533 let new_val = state.get_at(state.top_idx() - 1);
534 let mut target = crate::coro_lib::borrow_thread_rooted(state, &target_state);
535 if !target.get_stack_level(level, &mut ar) {
536 return Err(lua_vm::debug::arg_error_impl(
537 state,
538 arg + 1,
539 b"level out of range",
540 ));
541 }
542 check_cross_thread_stack(state, false, 1)?;
543 target.push(new_val);
544 let name = target.set_local_at(&ar, nvar)?;
545 if name.is_none() {
546 target.pop_n(1);
547 }
548 state.pop_n(1);
549 name
550 }
551 };
552
553 match name {
554 Some(n) => {
555 let ls = state.intern_str(&n)?;
556 state.push(LuaValue::Str(ls));
557 }
558 None => {
559 state.push(LuaValue::Nil);
560 }
561 }
562 Ok(1)
563}
564
565fn aux_upvalue(state: &mut LuaState, get: bool) -> Result<usize, LuaError> {
576 let n = state.check_arg_integer(2)? as i32;
577 state.check_arg_type(1, LuaType::Function)?;
578
579 let name: Option<Vec<u8>> = if get {
580 state.get_upvalue(1, n)?
582 } else {
583 state.set_upvalue(1, n)?
585 };
586
587 let name_ref = match name {
588 Some(n) => n,
589 None => return Ok(0),
590 };
591
592 let ls = state.intern_str(&name_ref)?;
593 state.push(LuaValue::Str(ls));
594
595 if get {
598 state.insert(-2)?;
599 }
600
601 Ok(if get { 2 } else { 1 })
602}
603
604pub(crate) fn get_upvalue(state: &mut LuaState) -> Result<usize, LuaError> {
607 aux_upvalue(state, true)
608}
609
610pub(crate) fn set_upvalue(state: &mut LuaState) -> Result<usize, LuaError> {
614 state.check_arg_any(3)?;
615 aux_upvalue(state, false)
616}
617
618fn check_upval(
623 state: &mut LuaState,
624 argf: i32,
625 argnup: i32,
626 require_valid: bool,
627) -> Result<(Option<UpvalId>, i32), LuaError> {
628 let nup = state.check_arg_integer(argnup)? as i32;
629 state.check_arg_type(argf, LuaType::Function)?;
630 let id: Option<UpvalId> = match state.upvalue_id(argf, nup) {
635 Ok(p) if p.is_null() => None,
636 Ok(p) => Some(p as usize),
637 Err(_) => None,
638 };
639 if require_valid && id.is_none() {
640 return Err(lua_vm::debug::arg_error_impl(
641 state,
642 argnup,
643 b"invalid upvalue index",
644 ));
645 }
646 Ok((id, nup))
647}
648
649pub(crate) fn upvalue_id(state: &mut LuaState) -> Result<usize, LuaError> {
653 let (id, _nup) = check_upval(state, 1, 2, false)?;
654 match id {
655 Some(uid) => {
656 lua_vm::api::push_light_userdata(state, uid as *mut core::ffi::c_void);
657 }
658 None => {
659 state.push_fail()?;
660 }
661 }
662 Ok(1)
663}
664
665pub(crate) fn upvalue_join(state: &mut LuaState) -> Result<usize, LuaError> {
669 let (_id1, n1) = check_upval(state, 1, 2, true)?;
670 let (_id2, n2) = check_upval(state, 3, 4, true)?;
671 if state.is_c_function_at(1) {
672 return Err(lua_vm::debug::arg_error_impl(
673 state,
674 1,
675 b"Lua function expected",
676 ));
677 }
678 if state.is_c_function_at(3) {
679 return Err(lua_vm::debug::arg_error_impl(
680 state,
681 3,
682 b"Lua function expected",
683 ));
684 }
685 state.join_upvalues(1, n1, 3, n2)?;
686 Ok(0)
687}
688
689pub(crate) fn hookf(state: &mut LuaState, event: i32, currentline: i32) -> Result<(), LuaError> {
695 state.get_registry_field(HOOKKEY)?;
696 state.push_thread()?;
697 if state.raw_get(-2)? == LuaType::Function {
698 let event_idx = event.clamp(0, HOOKNAMES.len() as i32 - 1) as usize;
699 let event_str = state.intern_str(HOOKNAMES[event_idx])?;
700 state.push(LuaValue::Str(event_str));
701
702 if currentline >= 0 {
703 state.push(LuaValue::Int(currentline as i64));
704 } else {
705 state.push(LuaValue::Nil);
706 }
707
708 state.call(2, 0)?;
709 }
710 Ok(())
713}
714
715fn make_mask(smask: &[u8], count: i32) -> u32 {
719 let mut mask: u32 = 0;
720 if smask.contains(&b'c') {
721 mask |= MASK_CALL;
722 }
723 if smask.contains(&b'r') {
724 mask |= MASK_RET;
725 }
726 if smask.contains(&b'l') {
727 mask |= MASK_LINE;
728 }
729 if count > 0 {
730 mask |= MASK_COUNT;
731 }
732 mask
733}
734
735fn unmake_mask(mask: u32) -> Vec<u8> {
739 let mut smask = Vec::with_capacity(3);
740 if mask & MASK_CALL != 0 {
741 smask.push(b'c');
742 }
743 if mask & MASK_RET != 0 {
744 smask.push(b'r');
745 }
746 if mask & MASK_LINE != 0 {
747 smask.push(b'l');
748 }
749 smask
750}
751
752pub(crate) fn set_hook(state: &mut LuaState) -> Result<usize, LuaError> {
756 let (arg, other_thread) = getthread(state);
757 let target_is_self = other_thread.is_none();
758
759 let hook_active: bool;
760 let mask: u32;
761 let count: i32;
762
763 if matches!(state.type_at(arg + 1), LuaType::None | LuaType::Nil) {
764 lua_vm::api::set_top(state, arg + 1)?;
765 hook_active = false;
766 mask = 0;
767 count = 0;
768 } else {
769 let smask: Vec<u8> = state.check_arg_string(arg + 2)?.to_vec();
770 state.check_arg_type(arg + 1, LuaType::Function)?;
771 count = state.opt_arg_integer(arg + 3, 0)? as i32;
772 hook_active = true;
773 mask = make_mask(&smask, count);
774 }
775
776 if !state.get_or_create_registry_subtable(HOOKKEY)? {
777 let k = state.intern_str(b"k")?;
780 state.push(LuaValue::Str(k));
781 state.set_field(-2, b"__mode")?;
782 state.push_value_at(-1)?;
783 state.set_metatable(-2)?;
784 }
785
786 check_cross_thread_stack(state, target_is_self, 1)?;
787 let target_state = resolve_debug_thread_target(state, &other_thread);
788 match &target_state {
789 DebugThreadTarget::Other(st) => {
790 st.borrow_mut().ensure_stack(1, "stack overflow")?;
791 }
792 DebugThreadTarget::Current => {}
793 DebugThreadTarget::Unavailable => {}
794 }
795
796 if target_is_self {
797 state.push_thread()?;
798 } else {
799 let thr = other_thread
806 .clone()
807 .expect("other_thread is Some when target_is_self is false");
808 state.push(lua_types::value::LuaValue::Thread(thr));
809 }
810 state.push_value_at(arg + 1)?;
811 state.raw_set(-3)?;
812
813 let hook_box: Option<Box<dyn FnMut(&mut LuaState, &lua_vm::debug::LuaDebug)>> = if hook_active {
814 Some(Box::new(|st, ar| {
815 let _ = hookf(st, ar.event, ar.currentline);
816 }))
817 } else {
818 None
819 };
820 match target_state {
821 DebugThreadTarget::Current => {
822 lua_vm::debug::set_hook(state, hook_box, mask as i32, count);
823 }
824 DebugThreadTarget::Other(target_state) => {
825 lua_vm::debug::set_hook(&mut target_state.borrow_mut(), hook_box, mask as i32, count);
826 }
827 DebugThreadTarget::Unavailable => {
828 return Ok(0);
832 }
833 }
834
835 Ok(0)
836}
837
838pub(crate) fn get_hook(state: &mut LuaState) -> Result<usize, LuaError> {
842 let (_arg, other_thread) = getthread(state);
843 let target_is_self = other_thread.is_none();
844 let target_state = resolve_debug_thread_target(state, &other_thread);
845
846 let (mask, hook_is_set, hook_is_internal, hook_count) = match target_state {
847 DebugThreadTarget::Current => (
848 state.get_hook_mask(),
849 state.hook_is_set(),
850 state.hook_is_internal_lua_hook(),
851 state.get_hook_count(),
852 ),
853 DebugThreadTarget::Other(target_state) => {
854 let mut target_state = target_state.borrow_mut();
855 (
856 target_state.get_hook_mask(),
857 target_state.hook_is_set(),
858 target_state.hook_is_internal_lua_hook(),
859 target_state.get_hook_count(),
860 )
861 }
862 DebugThreadTarget::Unavailable => (0u32, false, false, 0i32),
863 };
864
865 if !hook_is_set {
866 state.push_fail()?;
867 return Ok(1);
868 }
869
870 if !hook_is_internal {
872 let s = state.intern_str(b"external hook")?;
873 state.push(LuaValue::Str(s));
874 } else {
875 state.get_registry_field(HOOKKEY)?;
876 check_cross_thread_stack(state, target_is_self, 1)?;
877 if target_is_self {
878 state.push_thread()?;
879 } else {
880 let key_thread = other_thread
881 .expect("other_thread is Some when target_is_self is false")
882 .clone();
883 state.push(lua_types::value::LuaValue::Thread(key_thread));
884 }
885 state.raw_get(-2)?;
886 state.remove(-2)?;
887 }
888
889 let smask = unmake_mask(mask);
890 let ls = state.intern_str(&smask)?;
891 state.push(LuaValue::Str(ls));
892
893 state.push(LuaValue::Int(hook_count as i64));
894
895 Ok(3)
896}
897
898pub(crate) fn debug_interactive(state: &mut LuaState) -> Result<usize, LuaError> {
905 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
906 {
907 let _ = state;
908 return Err(LuaError::runtime(format_args!(
909 "debug.debug interactive stdin not available in this host"
910 )));
911 }
912
913 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
914 {
915 let stdin = io::stdin();
916 loop {
917 eprint!("lua_debug> ");
918 let _ = io::stderr().flush();
919
920 let mut line = String::new();
925 let n = stdin
926 .lock()
927 .read_line(&mut line)
928 .map_err(|e| LuaError::runtime(format_args!("stdin read error: {}", e)))?;
929
930 if n == 0 || line == "cont\n" {
931 return Ok(0);
932 }
933
934 let bytes: &[u8] = line.as_bytes();
935
936 let result = state
939 .load_buffer(bytes, b"=(debug command)", None)
940 .and_then(|_| state.protected_call(0, 0, 0));
941
942 if let Err(_) = result {
943 eprintln!("(error in debug command)");
948 state.pop_n(1);
949 }
950
951 lua_vm::api::set_top(state, 0)?;
952 }
953 }
954}
955
956pub(crate) fn traceback(state: &mut LuaState) -> Result<usize, LuaError> {
963 let (arg, other_thread) = getthread(state);
964 let target_is_self = other_thread.is_none();
965
966 let msg_owned: Option<Vec<u8>> = state
968 .to_lua_string(arg + 1)
969 .map(|s: GcRef<LuaString>| s.as_bytes().to_vec());
970
971 let arg1_ty = state.type_at(arg + 1);
972 if msg_owned.is_none() && !matches!(arg1_ty, LuaType::None | LuaType::Nil) {
973 state.push_value_at(arg + 1)?;
974 } else {
975 let default_level: i64 = if target_is_self { 1 } else { 0 };
976 let level = state.opt_arg_integer(arg + 2, default_level)? as i32;
977
978 match resolve_debug_thread_target(state, &other_thread) {
979 DebugThreadTarget::Current => {
980 crate::auxlib::traceback(state, None, msg_owned.as_deref(), level)?;
981 }
982 DebugThreadTarget::Other(target_state) => {
983 let mut target_state = crate::coro_lib::borrow_thread_rooted(state, &target_state);
984 crate::auxlib::traceback(
985 state,
986 Some(&mut *target_state),
987 msg_owned.as_deref(),
988 level,
989 )?;
990 }
991 DebugThreadTarget::Unavailable => {
992 crate::auxlib::traceback(state, None, msg_owned.as_deref(), level)?;
993 }
994 }
995 }
996 Ok(1)
997}
998
999pub(crate) fn set_c_stack_limit(state: &mut LuaState) -> Result<usize, LuaError> {
1003 let limit = state.check_arg_integer(1)? as i32;
1004 let res = state.set_c_stack_limit(limit)?;
1005 state.push(LuaValue::Int(res as i64));
1006 Ok(1)
1007}
1008
1009pub(crate) const DBLIB: &[(&[u8], LibFn)] = &[
1014 (b"debug", debug_interactive as LibFn),
1015 (b"getuservalue", get_uservalue as LibFn),
1016 (b"gethook", get_hook as LibFn),
1017 (b"getinfo", get_info as LibFn),
1018 (b"getlocal", get_local as LibFn),
1019 (b"getregistry", get_registry as LibFn),
1020 (b"getmetatable", get_metatable as LibFn),
1021 (b"getupvalue", get_upvalue as LibFn),
1022 (b"upvaluejoin", upvalue_join as LibFn),
1023 (b"upvalueid", upvalue_id as LibFn),
1024 (b"setuservalue", set_uservalue as LibFn),
1025 (b"sethook", set_hook as LibFn),
1026 (b"setlocal", set_local as LibFn),
1027 (b"setmetatable", set_metatable as LibFn),
1028 (b"setupvalue", set_upvalue as LibFn),
1029 (b"traceback", traceback as LibFn),
1030 (b"setcstacklimit", set_c_stack_limit as LibFn),
1031];
1032
1033pub fn open_debug(state: &mut LuaState) -> Result<usize, LuaError> {
1037 state.new_lib(DBLIB)?;
1038 Ok(1)
1039}
1040
1041