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