1#[allow(unused_imports)] use crate::prelude::*;
11use crate::state::{
12 CallInfo, GcRef, LuaClosure, LuaClosureLua, LuaProto, LuaState, LuaTable, LuaValue,
13 CIST_FIN, CIST_HOOKED, CIST_HOOKYIELD, CIST_TAIL, CIST_TRAN,
14};
15use lua_types::{CallInfoIdx, StackIdx, LuaString};
16use lua_types::error::LuaError;
17use lua_types::opcode::Instruction;
18use crate::vm::InstructionExt;
19
20const ABS_LINE_INFO: i8 = -0x80_i8;
36
37const MAX_IWTH_ABS: i32 = 128;
39
40const LUA_IDSIZE: usize = 60;
42
43const LUA_MASKLINE: u8 = 1 << 2;
45const LUA_MASKCOUNT: u8 = 1 << 3;
46
47const LUA_HOOKLINE: i32 = 2;
48const LUA_HOOKCOUNT: i32 = 3;
49
50const LUA_ENV: &[u8] = b"_ENV";
52
53fn runtime_bytes(msg: Vec<u8>) -> LuaError {
60 LuaError::Runtime(lua_types::LuaValue::Str(lua_types::GcRef::new(
61 lua_types::LuaString::from_bytes(msg),
62 )))
63}
64
65pub(crate) fn prefixed_runtime_pub(state: &LuaState, msg: Vec<u8>) -> LuaError {
73 prefixed_runtime(state, msg)
74}
75
76fn prefixed_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
77 let ci_idx = state.current_ci_idx();
78 let ci = state.get_ci(ci_idx).clone();
79 if !ci.is_lua() {
80 return runtime_bytes(msg);
81 }
82 let proto = ci_lua_proto(&ci, state);
83 let src = proto.source_string();
84 let line = get_current_line(&ci, state);
85 let prefixed = add_info(
86 None,
87 &msg,
88 src.map(|s| &**s),
89 line,
90 );
91 runtime_bytes(prefixed)
92}
93
94pub fn c_api_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
95 let ci_idx = state.current_ci_idx();
96 if let Some(parent_idx) = state.prev_ci(ci_idx) {
97 let parent_ci = state.get_ci(parent_idx).clone();
98 if parent_ci.is_lua() {
99 let proto = ci_lua_proto(&parent_ci, state);
100 let src = proto.source_string();
101 let line = get_current_line(&parent_ci, state);
102 let prefixed = add_info(None, &msg, src.map(|s| &**s), line);
103 return runtime_bytes(prefixed);
104 }
105 }
106 runtime_bytes(msg)
107}
108
109#[allow(dead_code)]
119fn find_func_in_table(table: &LuaTable, target: &LuaValue, prefix: &[u8], depth: u8) -> Option<Vec<u8>> {
120 let mut key = LuaValue::Nil;
121 loop {
122 let (k, v) = match table.next_pair(&key) {
123 Some(pair) => pair,
124 None => break,
125 };
126 if !matches!(v, LuaValue::Nil) {
127 let key_bytes: Option<Vec<u8>> = match &k {
128 LuaValue::Str(s) => Some(s.as_bytes().to_vec()),
129 _ => None,
130 };
131 if let Some(kb) = key_bytes {
132 if &v == target {
133 if prefix.is_empty() {
134 return Some(kb);
135 }
136 let mut result = prefix.to_vec();
137 result.push(b'.');
138 result.extend_from_slice(&kb);
139 return Some(result);
140 }
141 if depth > 0 {
142 if let LuaValue::Table(sub) = &v {
143 let new_prefix = if prefix.is_empty() {
144 kb.clone()
145 } else {
146 let mut p = prefix.to_vec();
147 p.push(b'.');
148 p.extend_from_slice(&kb);
149 p
150 };
151 if let Some(name) = find_func_in_table(&**sub, target, &new_prefix, depth - 1) {
152 return Some(name);
153 }
154 }
155 }
156 }
157 }
158 key = k;
159 }
160 None
161}
162
163#[allow(dead_code)]
171fn find_func_name_in_globals(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
172 let globals = state.global().globals.clone();
173 if let LuaValue::Table(globals_table) = globals {
174 find_func_in_table(&*globals_table, func_val, b"", 1)
175 } else {
176 None
177 }
178}
179
180fn find_func_name_in_loaded(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
187 let registry = state.global().l_registry.clone();
188 let loaded = match registry {
189 LuaValue::Table(ref reg_table) => reg_table.get_str_bytes(b"_LOADED"),
190 _ => return None,
191 };
192 let loaded_table = match loaded {
193 LuaValue::Table(t) => t,
194 _ => return None,
195 };
196 find_func_in_table(&*loaded_table, func_val, b"", 1)
197}
198
199pub fn arg_error_impl(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> LuaError {
203 let mut ar = LuaDebug::default();
204 if !get_stack(state, 0, &mut ar) {
205 let msg = format!("bad argument #{} ({})", arg, String::from_utf8_lossy(extramsg));
206 return c_api_runtime(state, msg.into_bytes());
207 }
208 get_info(state, b"n", &mut ar);
209 if ar.namewhat.as_deref() == Some(b"method") {
210 arg -= 1;
211 if arg == 0 {
212 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
213 let msg = format!(
214 "calling '{}' on bad self ({})",
215 String::from_utf8_lossy(&name),
216 String::from_utf8_lossy(extramsg)
217 );
218 return c_api_runtime(state, msg.into_bytes());
219 }
220 }
221 let fname = ar.name.clone().or_else(|| {
222 let ci_idx = ar.i_ci?;
223 let func_slot = state.get_ci(ci_idx).func;
224 let func_val = state.get_at(func_slot).clone();
225 let found = find_func_name_in_loaded(state, &func_val)?;
226 if found.starts_with(b"_G.") {
227 Some(found[3..].to_vec())
228 } else {
229 Some(found)
230 }
231 }).unwrap_or_else(|| b"?".to_vec());
232 let msg = format!(
233 "bad argument #{} to '{}' ({})",
234 arg,
235 String::from_utf8_lossy(&fname),
236 String::from_utf8_lossy(extramsg)
237 );
238 c_api_runtime(state, msg.into_bytes())
239}
240
241pub struct LuaDebug {
252 pub event: i32,
253 pub name: Option<Vec<u8>>,
254 pub namewhat: Option<&'static [u8]>,
255 pub what: Option<&'static [u8]>,
256 pub source: Option<Vec<u8>>,
257 pub srclen: usize,
258 pub currentline: i32,
259 pub linedefined: i32,
260 pub lastlinedefined: i32,
261 pub nups: u8,
262 pub nparams: u8,
263 pub isvararg: bool,
264 pub istailcall: bool,
265 pub ftransfer: u16,
266 pub ntransfer: u16,
267 pub short_src: [u8; LUA_IDSIZE],
268 pub i_ci: Option<CallInfoIdx>,
270}
271
272impl Default for LuaDebug {
273 fn default() -> Self {
274 LuaDebug {
275 event: 0,
276 name: None,
277 namewhat: None,
278 what: None,
279 source: None,
280 srclen: 0,
281 currentline: -1,
282 linedefined: -1,
283 lastlinedefined: -1,
284 nups: 0,
285 nparams: 0,
286 isvararg: false,
287 istailcall: false,
288 ftransfer: 0,
289 ntransfer: 0,
290 short_src: [0u8; LUA_IDSIZE],
291 i_ci: None,
292 }
293 }
294}
295
296#[inline]
300fn is_lua_closure(cl: Option<&LuaClosure>) -> bool {
301 matches!(cl, Some(LuaClosure::Lua(_)))
302}
303
304fn current_pc(ci: &CallInfo) -> i32 {
319 debug_assert!(ci.is_lua());
320 ci.saved_pc().saturating_sub(1) as i32
323}
324
325fn get_baseline(f: &LuaProto, pc: i32, basepc: &mut i32) -> i32 {
333 if f.abslineinfo.is_empty() || pc < f.abslineinfo[0].pc {
334 *basepc = -1;
335 return f.linedefined;
336 }
337 let mut i = (pc as u32 / MAX_IWTH_ABS as u32).saturating_sub(1) as usize;
339 debug_assert!(
340 i < f.abslineinfo.len() && f.abslineinfo[i].pc <= pc,
341 "getbaseline: estimate is not a lower bound"
342 );
343 while i + 1 < f.abslineinfo.len() && pc >= f.abslineinfo[i + 1].pc {
344 i += 1;
345 }
346 *basepc = f.abslineinfo[i].pc;
347 f.abslineinfo[i].line
348}
349
350pub(crate) fn get_func_line(f: &LuaProto, pc: i32) -> i32 {
354 if f.lineinfo.is_empty() {
355 return -1;
356 }
357 let mut basepc: i32 = 0;
358 let mut baseline = get_baseline(f, pc, &mut basepc);
359 while basepc < pc {
362 basepc += 1;
363 debug_assert!(
364 f.lineinfo[basepc as usize] != ABS_LINE_INFO,
365 "get_func_line: hit ABSLINEINFO in incremental walk"
366 );
367 baseline += f.lineinfo[basepc as usize] as i32;
368 }
369 baseline
370}
371
372fn get_current_line(ci: &CallInfo, state: &LuaState) -> i32 {
375 let proto = ci_lua_proto(ci, state);
376 get_func_line(&proto, current_pc(ci))
377}
378
379fn set_traps(state: &mut LuaState) {
388 for ci in state.call_stack_mut().iter_mut() {
392 if ci.is_lua() {
393 ci.set_trap(true);
394 }
395 }
396}
397
398pub fn set_hook(
401 state: &mut LuaState,
402 func: Option<Box<dyn FnMut(&mut LuaState, &LuaDebug)>>,
403 mask: i32,
404 count: i32,
405) {
406 let (func, mask) = if func.is_none() || mask == 0 {
407 (None, 0i32)
408 } else {
409 (func, mask)
410 };
411 state.set_hook(func);
412 state.set_base_hook_count(count);
413 state.reset_hook_count();
415 state.set_hook_mask(mask as u8);
417 if mask != 0 {
418 set_traps(state);
419 }
420}
421
422pub fn get_hook_installed(state: &LuaState) -> bool {
429 state.hook().is_some()
430}
431
432pub fn get_hook_mask(state: &LuaState) -> i32 {
435 state.hook_mask() as i32
436}
437
438pub fn get_hook_count(state: &LuaState) -> i32 {
441 state.base_hook_count()
442}
443
444pub fn get_stack(state: &LuaState, level: i32, ar: &mut LuaDebug) -> bool {
451 if level < 0 {
452 return false;
453 }
454 let mut remaining = level;
455 let mut ci_idx = state.current_ci_idx();
456 loop {
457 if remaining == 0 {
458 break;
459 }
460 match state.prev_ci(ci_idx) {
461 Some(prev) => {
462 ci_idx = prev;
463 remaining -= 1;
464 }
465 None => {
466 return false;
467 }
468 }
469 }
470 if !state.is_base_ci(ci_idx) {
471 ar.i_ci = Some(ci_idx);
472 true
473 } else {
474 false
475 }
476}
477
478fn upval_name(p: &LuaProto, uv: usize) -> &[u8] {
483 debug_assert!(uv < p.upvalues.len(), "upval_name: index out of range");
486 p.upvalues[uv].name.as_ref().map_or(b"?" as &[u8], |s| s.as_bytes())
489}
490
491fn find_vararg(state: &LuaState, ci: &CallInfo, n: i32) -> Option<(StackIdx, &'static [u8])> {
498 let proto = ci_lua_proto(ci, state);
499 if proto.is_vararg {
500 let nextra = ci.nextra_args();
501 if n >= -(nextra as i32) {
502 let pos = ci.func - (nextra + n + 1);
505 return Some((pos, b"(vararg)" as &[u8]));
506 }
507 }
508 None
509}
510
511pub(crate) fn find_local(
524 state: &LuaState,
525 ci_idx: CallInfoIdx,
526 n: i32,
527 pos: Option<&mut StackIdx>,
528) -> Option<Vec<u8>> {
529 let ci = state.get_ci(ci_idx);
530 let base = ci.func + 1;
531 let mut name: Option<Vec<u8>> = None;
532
533 if ci.is_lua() {
534 if n < 0 {
535 if let Some((vpos, vname)) = find_vararg(state, ci, n) {
536 if let Some(out_pos) = pos {
537 *out_pos = vpos;
538 }
539 return Some(vname.to_vec());
540 }
541 return None;
542 } else {
543 let proto = ci_lua_proto(ci, state);
544 let pc = current_pc(ci);
545 name = crate::func::get_local_name(&proto, n, pc).map(|s| s.to_vec());
546 }
547 }
548
549 if name.is_none() {
550 let limit: u32 = if ci_idx == state.current_ci_idx() {
551 state.top_idx().0
552 } else {
553 ci.next
554 .map(|next| state.get_ci(next).func.0)
555 .unwrap_or_else(|| state.top_idx().0)
556 };
557 if n > 0 && limit.saturating_sub(base.0) >= n as u32 {
558 name = Some(if ci.is_lua() { b"(temporary)".to_vec() } else { b"(C temporary)".to_vec() });
559 } else {
560 return None;
561 }
562 }
563
564 if let Some(out_pos) = pos {
565 *out_pos = base + (n - 1);
566 }
567 name
568}
569
570pub fn get_local(state: &mut LuaState, ar: Option<&LuaDebug>, n: i32) -> Option<Vec<u8>> {
575 if ar.is_none() {
576 let top_val = state.peek_top();
578 if !matches!(top_val, LuaValue::Function(LuaClosure::Lua(_))) {
579 return None;
580 }
581 let name_owned: Option<Vec<u8>> = {
584 let cl = match top_val {
585 LuaValue::Function(LuaClosure::Lua(ref cl)) => cl.clone(),
586 _ => unreachable!(),
587 };
588 get_local_name_from_closure(&cl, n, 0).map(|s| s.to_vec())
590 };
591 return name_owned;
592 }
593
594 let ar = ar.unwrap();
595 let ci_idx = ar.i_ci?;
596 let mut pos = StackIdx(0);
597 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
600
601 if name_owned.is_some() {
602 let val = state.get_at(pos).clone();
603 state.push(val);
604 }
605 name_owned
606}
607
608pub fn set_local(state: &mut LuaState, ar: &LuaDebug, n: i32) -> Option<Vec<u8>> {
612 let ci_idx = ar.i_ci?;
613 let mut pos = StackIdx(0);
614 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
616 if name_owned.is_some() {
617 let val = state.get_at(state.top_idx() - 1).clone();
618 state.set_at(pos, val);
619 state.pop_n(1);
620 }
621 name_owned
622}
623
624fn func_info(ar: &mut LuaDebug, cl: Option<&LuaClosure>) {
629 if !is_lua_closure(cl) {
630 ar.source = Some(b"=[C]".to_vec());
632 ar.srclen = b"=[C]".len();
633 ar.linedefined = -1;
634 ar.lastlinedefined = -1;
635 ar.what = Some(b"C");
636 } else {
637 let lua_cl = match cl {
638 Some(LuaClosure::Lua(cl)) => cl,
639 _ => unreachable!(),
640 };
641 let proto: &LuaProto = &lua_cl.proto;
643 if let Some(src) = proto.source_string() {
645 ar.source = Some(src.as_bytes().to_vec());
646 ar.srclen = src.as_bytes().len();
647 } else {
648 ar.source = Some(b"=?".to_vec());
649 ar.srclen = b"=?".len();
650 }
651 ar.linedefined = proto.linedefined;
652 ar.lastlinedefined = proto.lastlinedefined;
653 ar.what = Some(if ar.linedefined == 0 { b"main" } else { b"Lua" });
654 }
655 chunk_id(&mut ar.short_src, ar.source.as_deref().unwrap_or(b"?"), ar.srclen);
657}
658
659fn next_line(p: &LuaProto, currentline: i32, pc: usize) -> i32 {
663 if p.lineinfo.get(pc).copied() != Some(ABS_LINE_INFO) {
665 currentline + p.lineinfo[pc] as i32
666 } else {
667 get_func_line(p, pc as i32)
668 }
669}
670
671fn collect_valid_lines(state: &mut LuaState, cl: Option<&LuaClosure>) -> Result<(), LuaError> {
675 if !is_lua_closure(cl) {
676 state.push(LuaValue::Nil);
678 return Ok(());
679 }
680 let lua_cl = match cl {
681 Some(LuaClosure::Lua(cl)) => cl.clone(),
682 _ => unreachable!(),
683 };
684 let proto: GcRef<LuaProto> = lua_cl.proto.clone();
686 let p: &LuaProto = &proto;
687
688 let mut currentline = p.linedefined;
689
690 let t = state.new_table();
692 state.push(LuaValue::Table(t.clone()));
694
695 if !p.lineinfo.is_empty() {
696 let v = LuaValue::Bool(true);
698
699 let start_i = if !p.is_vararg {
700 0usize
701 } else {
702 debug_assert!(
704 p.code.first().map(|i| i.is_vararg_prep()).unwrap_or(false),
705 "collect_valid_lines: first instruction of vararg should be OP_VARARGPREP"
706 );
707 currentline = next_line(p, currentline, 0);
708 1usize
709 };
710
711 for i in start_i..p.lineinfo.len() {
713 currentline = next_line(p, currentline, i);
714 t.raw_set_int(state, currentline as i64, v.clone())?;
716 }
717 }
718 Ok(())
719}
720
721fn get_func_name<'a>(
727 state: &'a LuaState,
728 ci: Option<&CallInfo>,
729 name: &mut Option<Vec<u8>>,
730) -> Option<&'static [u8]> {
731 let ci = ci?;
734 if ci.callstatus & CIST_TAIL != 0 {
735 return None;
736 }
737 let prev_idx = ci.previous?;
740 let prev_ci = state.get_ci(prev_idx).clone();
741 funcname_from_call(state, &prev_ci, name)
742}
743
744fn aux_get_info(
747 state: &LuaState,
748 what: &[u8],
749 ar: &mut LuaDebug,
750 cl: Option<&LuaClosure>,
751 ci: Option<&CallInfo>,
752) -> bool {
753 let mut status = true;
754 for &ch in what {
755 match ch {
756 b'S' => {
757 func_info(ar, cl);
758 }
759 b'l' => {
760 ar.currentline = match ci {
761 Some(ci) if ci.is_lua() => get_current_line(ci, state),
762 _ => -1,
763 };
764 }
765 b'u' => {
766 ar.nups = cl.map_or(0, |c| c.nupvalues() as u8);
767 match cl {
768 Some(LuaClosure::Lua(lua_cl)) => {
769 ar.isvararg = lua_cl.proto.is_vararg;
771 ar.nparams = lua_cl.proto.numparams;
772 }
773 _ => {
774 ar.isvararg = true;
775 ar.nparams = 0;
776 }
777 }
778 }
779 b't' => {
780 ar.istailcall = ci.map_or(false, |ci| ci.callstatus & CIST_TAIL != 0);
781 }
782 b'n' => {
783 let mut name: Option<Vec<u8>> = None;
784 ar.namewhat = get_func_name(state, ci, &mut name);
785 if ar.namewhat.is_none() {
786 ar.namewhat = Some(b"");
787 ar.name = None;
788 } else {
789 ar.name = name;
790 }
791 }
792 b'r' => match ci {
794 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
795 ar.ftransfer = ci.transfer_ftransfer();
797 ar.ntransfer = ci.transfer_ntransfer();
798 }
799 _ => {
800 ar.ftransfer = 0;
801 ar.ntransfer = 0;
802 }
803 },
804 b'L' | b'f' => {}
805 _ => {
806 status = false;
807 }
808 }
809 }
810 status
811}
812
813pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
816 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
817 let func_val = state.peek_at(state.top_idx() - 1).clone();
818 state.pop_n(1);
819 debug_assert!(
820 matches!(func_val, LuaValue::Function(_)),
821 "get_info: function expected"
822 );
823 let cl = match &func_val {
824 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => {
825 Some(match &func_val {
826 LuaValue::Function(c) => c.clone(),
827 _ => unreachable!(),
828 })
829 }
830 _ => None,
831 };
832 (cl, None, func_val, &what[1..])
833 } else {
834 let ci_idx = match ar.i_ci { Some(i) => i, None => return false };
835 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
836 debug_assert!(
837 matches!(func_val, LuaValue::Function(_)),
838 "get_info: non-function at ci->func"
839 );
840 let cl = match &func_val {
841 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => {
842 Some(match &func_val {
843 LuaValue::Function(c) => c.clone(),
844 _ => unreachable!(),
845 })
846 }
847 _ => None,
848 };
849 (cl, Some(ci_idx), func_val, what)
850 };
851
852 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
853 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
854
855 if what.contains(&b'f') {
856 state.push(func_val);
857 }
858 if what.contains(&b'L') {
859 let _ = collect_valid_lines(state, cl.as_ref());
861 }
862 status
863}
864
865#[inline]
871fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
872 if pc < jmptarget { -1 } else { pc }
873}
874
875fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
879 let mut setreg: i32 = -1;
880 let mut jmptarget: i32 = 0;
881
882 let effective_lastpc = if p.code.get(lastpc as usize).map_or(false, |i| i.is_mm_mode()) {
885 lastpc - 1
886 } else {
887 lastpc
888 };
889
890 for pc in 0..effective_lastpc {
891 let instr = p.code[pc as usize];
892 let op = instr.opcode();
893 let a = instr.arg_a() as i32;
894
895 let change = match op {
896 OpCode::LoadNil => {
897 let b = instr.arg_b() as i32;
898 a <= reg && reg <= a + b
899 }
900 OpCode::TForCall => reg >= a + 2,
901 OpCode::Call | OpCode::TailCall => reg >= a,
902 OpCode::Jmp => {
903 let b = instr.arg_s_j();
904 let dest = pc + 1 + b;
905 if dest <= effective_lastpc && dest > jmptarget {
906 jmptarget = dest;
907 }
908 false
909 }
910 _ => {
911 instr.test_a_mode() && reg == a
914 }
915 };
916
917 if change {
918 setreg = filter_pc(pc, jmptarget);
919 }
920 }
921 setreg
922}
923
924fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
929 match p.k.get(index) {
932 Some(LuaValue::Str(s)) => {
933 *name = s.as_bytes();
935 Some(b"constant")
936 }
937 _ => {
938 *name = b"?";
939 None
940 }
941 }
942}
943
944fn basic_get_obj_name<'a>(
948 p: &'a LuaProto,
949 ppc: &mut i32,
950 reg: i32,
951 name: &mut &'a [u8],
952) -> Option<&'static [u8]> {
953 let pc = *ppc;
954 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
956 *name = local_name;
957 return Some(b"local");
958 }
959
960 *ppc = find_set_reg(p, pc, reg);
961 let pc = *ppc;
962
963 if pc == -1 {
964 return None;
965 }
966
967 let instr = p.code[pc as usize];
968 let op = instr.opcode();
969 match op {
970 OpCode::Move => {
971 let b = instr.arg_b() as i32;
972 if b < instr.arg_a() as i32 {
973 return basic_get_obj_name(p, ppc, b, name);
974 }
975 }
976 OpCode::GetUpVal => {
977 *name = upval_name(p, instr.arg_b() as usize);
978 return Some(b"upvalue");
979 }
980 OpCode::LoadK => {
981 return kname(p, instr.arg_bx() as usize, name);
982 }
983 OpCode::LoadKx => {
984 let next = p.code[(pc + 1) as usize];
985 return kname(p, next.arg_ax() as usize, name);
986 }
987 _ => {}
988 }
989 None
990}
991
992fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
996 let mut pc = pc;
997 let what = basic_get_obj_name(p, &mut pc, c, name);
999 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1000 *name = b"?";
1001 }
1002}
1003
1004fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1007 let c = instr.arg_c() as i32;
1008 if instr.arg_k() != 0 {
1010 kname(p, c as usize, name);
1011 } else {
1012 rname(p, pc, c, name);
1013 }
1014}
1015
1016fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1020 let t = instr.arg_b() as usize;
1021 let mut name: &[u8] = b"?";
1022 if isup {
1023 name = upval_name(p, t);
1024 } else {
1025 let mut pc = pc;
1026 basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1027 }
1028 if name == LUA_ENV { b"global" } else { b"field" }
1029}
1030
1031fn get_obj_name<'a>(
1035 p: &'a LuaProto,
1036 lastpc: i32,
1037 reg: i32,
1038 name: &mut &'a [u8],
1039) -> Option<&'static [u8]> {
1040 let mut lastpc = lastpc;
1041 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1042 if kind.is_some() {
1043 return kind;
1044 }
1045
1046 if lastpc == -1 {
1047 return None;
1048 }
1049
1050 let instr = p.code[lastpc as usize];
1051 let op = instr.opcode();
1052 match op {
1053 OpCode::GetTabUp => {
1054 let k = instr.arg_c() as usize;
1055 kname(p, k, name);
1056 Some(is_env(p, lastpc, instr, true))
1057 }
1058 OpCode::GetTable => {
1059 let k = instr.arg_c() as i32;
1060 rname(p, lastpc, k, name);
1061 Some(is_env(p, lastpc, instr, false))
1062 }
1063 OpCode::GetI => {
1064 *name = b"integer index";
1065 Some(b"field")
1066 }
1067 OpCode::GetField => {
1068 let k = instr.arg_c() as usize;
1069 kname(p, k, name);
1070 Some(is_env(p, lastpc, instr, false))
1071 }
1072 OpCode::Self_ => {
1073 rkname(p, lastpc, instr, name);
1074 Some(b"method")
1075 }
1076 _ => None,
1077 }
1078}
1079
1080fn funcname_from_code<'a>(
1087 state: &LuaState,
1088 p: &'a LuaProto,
1089 pc: i32,
1090 name: &mut Option<Vec<u8>>,
1091) -> Option<&'static [u8]> {
1092 let instr = p.code[pc as usize];
1093 let op = instr.opcode();
1094
1095 match op {
1096 OpCode::Call | OpCode::TailCall => {
1097 let mut name_bytes: &[u8] = b"?";
1098 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1099 *name = Some(name_bytes.to_vec());
1100 kind
1101 }
1102 OpCode::TForCall => {
1103 *name = Some(b"for iterator".to_vec());
1104 Some(b"for iterator")
1105 }
1106 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1108 get_tm_name(state, TagMethod::Index, name)
1109 }
1110 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1111 get_tm_name(state, TagMethod::NewIndex, name)
1112 }
1113 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1114 let tm_idx = instr.arg_c() as u8;
1117 let tm = TagMethod::from_u8(tm_idx);
1118 get_tm_name(state, tm, name)
1119 }
1120 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1121 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1122 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1123 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1124 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1125 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1126 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1127 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1128 _ => None,
1129 }
1130}
1131
1132fn get_tm_name(
1138 state: &LuaState,
1139 tm: TagMethod,
1140 name: &mut Option<Vec<u8>>,
1141) -> Option<&'static [u8]> {
1142 let raw_bytes: Vec<u8> = state.global()
1146 .tm_name(tm)
1147 .map(|s| s.as_bytes().to_vec())
1148 .unwrap_or_default();
1149 let stripped = raw_bytes
1150 .strip_prefix(b"__")
1151 .unwrap_or(&raw_bytes)
1152 .to_vec();
1153 *name = Some(stripped);
1154 Some(b"metamethod")
1155}
1156
1157fn funcname_from_call<'a>(
1160 state: &'a LuaState,
1161 ci: &CallInfo,
1162 name: &mut Option<Vec<u8>>,
1163) -> Option<&'static [u8]> {
1164 if ci.callstatus & CIST_HOOKED != 0 {
1165 *name = Some(b"?".to_vec());
1166 return Some(b"hook");
1167 }
1168 if ci.callstatus & CIST_FIN != 0 {
1169 *name = Some(b"__gc".to_vec());
1170 return Some(b"metamethod");
1171 }
1172 if ci.is_lua() {
1173 let proto = ci_lua_proto(ci, state);
1174 return funcname_from_code(state, &proto, current_pc(ci), name);
1175 }
1176 None
1177}
1178
1179fn in_stack(ci: &CallInfo, val_idx: StackIdx) -> i32 {
1190 let base = StackIdx(ci.func.0 + 1);
1191 let ci_top = ci.top;
1194 let mut pos = 0i32;
1195 let mut cur = base;
1196 while cur.0 < ci_top.0 {
1197 if cur == val_idx {
1198 return pos;
1199 }
1200 cur = StackIdx(cur.0 + 1);
1201 pos += 1;
1202 }
1203 -1
1204}
1205
1206fn get_upval_name<'a>(
1215 ci: &CallInfo,
1216 val_idx: StackIdx,
1217 name: &mut &'a [u8],
1218 state: &'a LuaState,
1219) -> Option<&'static [u8]> {
1220 let proto = ci_lua_proto(ci, state);
1221 let lua_cl = match state.get_at(ci.func) {
1224 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1225 _ => return None,
1226 };
1227 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1228 let upval = upval_slot.get();
1229 let state = upval.slot().clone();
1230 if let lua_types::UpValState::Open { idx, .. } = state {
1231 if idx == val_idx {
1232 let _ = upval_name(&proto, i);
1235 *name = b"upvalue";
1236 return Some(b"upvalue");
1237 }
1238 }
1239 }
1240 None
1241}
1242
1243fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1248 match (kind, name) {
1249 (Some(k), Some(n)) => {
1250 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1251 out.extend_from_slice(b" (");
1252 out.extend_from_slice(k);
1253 out.extend_from_slice(b" '");
1254 out.extend_from_slice(n);
1255 out.extend_from_slice(b"')");
1256 out
1257 }
1258 _ => Vec::new(),
1259 }
1260}
1261
1262fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1266 let ci_idx = state.current_ci_idx();
1267 let ci = state.get_ci(ci_idx).clone();
1268 let mut kind: Option<&[u8]> = None;
1269 let mut name_owned: Vec<u8> = b"?".to_vec();
1270
1271 if ci.is_lua() {
1272 let mut up_name: &[u8] = b"?";
1273 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1274 if kind.is_some() {
1275 name_owned = up_name.to_vec();
1276 } else {
1277 let reg = in_stack(&ci, val_idx);
1278 if reg >= 0 {
1279 let proto = ci_lua_proto(&ci, state);
1280 let mut nref: &[u8] = b"?";
1281 let pc = current_pc(&ci);
1282 let k = get_obj_name(&proto, pc, reg, &mut nref);
1283 kind = k;
1284 if kind.is_some() {
1285 name_owned = nref.to_vec();
1286 }
1287 }
1288 }
1289 }
1290 format_var_info(kind, if kind.is_some() { Some(&name_owned) } else { None })
1291}
1292
1293fn typeerror_inner(
1298 state: &LuaState,
1299 val: &LuaValue,
1300 op: &[u8],
1301 extra: &[u8],
1302) -> LuaError {
1303 let t = state.obj_type_name(val);
1304 let mut msg = Vec::new();
1305 msg.extend_from_slice(b"attempt to ");
1306 msg.extend_from_slice(op);
1307 msg.extend_from_slice(b" a ");
1308 msg.extend_from_slice(&t);
1309 msg.extend_from_slice(b" value");
1310 msg.extend_from_slice(extra);
1311 prefixed_runtime(state, msg)
1312}
1313
1314pub(crate) fn type_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx, op: &[u8]) -> LuaError {
1318 let extra = var_info(state, val_idx);
1319 typeerror_inner(state, val, op, &extra)
1320}
1321
1322pub(crate) fn type_error_with_hint(
1328 state: &LuaState,
1329 val: &LuaValue,
1330 op: &[u8],
1331 kind: &[u8],
1332 name: &[u8],
1333) -> LuaError {
1334 let extra = format_var_info(Some(kind), Some(name));
1335 let t = obj_type_name_static(val);
1336 let mut msg = Vec::new();
1337 msg.extend_from_slice(b"attempt to ");
1338 msg.extend_from_slice(op);
1339 msg.extend_from_slice(b" a ");
1340 msg.extend_from_slice(t);
1341 msg.extend_from_slice(b" value");
1342 msg.extend_from_slice(&extra);
1343 prefixed_runtime(state, msg)
1344}
1345
1346fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1349 match val {
1350 LuaValue::Nil => b"nil",
1351 LuaValue::Bool(_) => b"boolean",
1352 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1353 LuaValue::Str(_) => b"string",
1354 LuaValue::Table(_) => b"table",
1355 LuaValue::Function(_) => b"function",
1356 LuaValue::UserData(_) => b"userdata",
1357 LuaValue::LightUserData(_) => b"light userdata",
1358 LuaValue::Thread(_) => b"thread",
1359 }
1360}
1361
1362pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1366 let ci_idx = state.current_ci_idx();
1367 let ci = state.get_ci(ci_idx).clone();
1368 let mut name: Option<Vec<u8>> = None;
1369 let kind = funcname_from_call(state, &ci, &mut name);
1370 let extra = if kind.is_some() {
1371 format_var_info(kind, name.as_deref())
1372 } else {
1373 var_info(state, val_idx)
1374 };
1375 typeerror_inner(state, val, b"call", &extra)
1376}
1377
1378pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1381 let t = crate::tagmethods::obj_type_name(state, val)
1382 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1383 let mut msg = Vec::new();
1384 msg.extend_from_slice(b"bad 'for' ");
1385 msg.extend_from_slice(what);
1386 msg.extend_from_slice(b" (number expected, got ");
1387 msg.extend_from_slice(&t);
1388 msg.push(b')');
1389 prefixed_runtime(state, msg)
1390}
1391
1392pub(crate) fn op_int_error(
1396 state: &LuaState,
1397 p1: &LuaValue,
1398 p1_idx: StackIdx,
1399 p2: &LuaValue,
1400 p2_idx: StackIdx,
1401 msg: &[u8],
1402) -> LuaError {
1403 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1405 (p1, p1_idx)
1406 } else {
1407 (p2, p2_idx)
1408 };
1409 type_error(state, bad_val, bad_idx, msg)
1410}
1411
1412pub(crate) fn to_int_error(
1418 state: &LuaState,
1419 p1: &LuaValue,
1420 p1_idx: Option<StackIdx>,
1421 _p2: &LuaValue,
1422 p2_idx: Option<StackIdx>,
1423) -> LuaError {
1424 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1425 p1_idx
1426 } else {
1427 p2_idx
1428 };
1429 let extra = match bad_idx {
1430 Some(idx) => var_info(state, idx),
1431 None => Vec::new(),
1432 };
1433 let mut msg = Vec::new();
1434 msg.extend_from_slice(b"number");
1435 msg.extend_from_slice(&extra);
1436 msg.extend_from_slice(b" has no integer representation");
1437 prefixed_runtime(state, msg)
1438}
1439
1440pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1443 let t1 = state.obj_type_name(p1);
1445 let t2 = state.obj_type_name(p2);
1446 let msg = if t1 == t2 {
1448 let mut m = Vec::new();
1449 m.extend_from_slice(b"attempt to compare two ");
1450 m.extend_from_slice(&t1);
1451 m.extend_from_slice(b" values");
1452 m
1453 } else {
1454 let mut m = Vec::new();
1455 m.extend_from_slice(b"attempt to compare ");
1456 m.extend_from_slice(&t1);
1457 m.extend_from_slice(b" with ");
1458 m.extend_from_slice(&t2);
1459 m
1460 };
1461 prefixed_runtime(state, msg)
1462}
1463
1464pub(crate) fn add_info(
1473 _state: Option<&mut LuaState>,
1474 msg: &[u8],
1475 src: Option<&LuaString>,
1476 line: i32,
1477) -> Vec<u8> {
1478 let mut buff = [0u8; LUA_IDSIZE];
1480 if let Some(src) = src {
1481 chunk_id(&mut buff, src.as_bytes(), src.len());
1484 } else {
1485 buff[0] = b'?';
1486 }
1487 let src_part = buff.iter().position(|&b| b == 0).map_or(&buff[..], |n| &buff[..n]);
1490 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1491 out.extend_from_slice(src_part);
1492 out.push(b':');
1493 let line_str = line.to_string();
1495 out.extend_from_slice(line_str.as_bytes());
1496 out.extend_from_slice(b": ");
1497 out.extend_from_slice(msg);
1498 out
1499}
1500
1501
1502fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1507 if p.lineinfo.is_empty() {
1508 return false;
1509 }
1510
1511 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1512 let mut delta: i32 = 0;
1513 let mut pc = oldpc;
1514 loop {
1515 pc += 1;
1516 if pc as usize >= p.lineinfo.len() {
1517 break;
1518 }
1519 let lineinfo = p.lineinfo[pc as usize];
1520 if lineinfo == ABS_LINE_INFO {
1521 break;
1522 }
1523 delta += lineinfo as i32;
1524 if pc == newpc {
1525 return delta != 0;
1526 }
1527 }
1528 }
1529 get_func_line(p, oldpc) != get_func_line(p, newpc)
1530}
1531
1532pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1538 let ci_idx = state.current_ci_idx();
1539 let ci = state.get_ci(ci_idx).clone();
1540 state.get_ci_mut(ci_idx).set_trap(true);
1541 let proto = ci_lua_proto(&ci, state);
1542
1543 if ci.saved_pc() == 0 {
1544 if proto.is_vararg {
1545 return Ok(0);
1546 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1547 state.hook_call(ci_idx)?;
1549 }
1550 }
1551 Ok(1)
1552}
1553
1554pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
1564 let ci_idx = state.current_ci_idx();
1565 let ci = state.get_ci(ci_idx).clone();
1566
1567 let mask = state.hook_mask();
1568
1569 if !state.allowhook {
1570 return Ok(1);
1571 }
1572
1573 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
1574 state.get_ci_mut(ci_idx).set_trap(false);
1575 return Ok(0);
1576 }
1577
1578 let next_pc = pc + 1;
1579 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
1580
1581 let counthook = if mask & LUA_MASKCOUNT != 0 {
1582 let hc = state.hook_count() - 1;
1583 state.set_hook_count(hc);
1584 hc == 0
1585 } else {
1586 false
1587 };
1588
1589 if counthook {
1590 state.reset_hook_count();
1591 } else if mask & LUA_MASKLINE == 0 {
1592 return Ok(1);
1593 }
1594
1595 if ci.callstatus & CIST_HOOKYIELD != 0 {
1596 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
1597 return Ok(1);
1598 }
1599
1600 if state.ci_lua_closure(ci_idx).is_none() {
1601 return Ok(1);
1602 }
1603
1604 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
1607 if !cur_instr.is_in_top() {
1608 let ci_top = state.get_ci(ci_idx).top;
1609 state.set_top(ci_top);
1610 }
1611
1612 if counthook {
1613 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
1615 }
1616
1617 if mask & LUA_MASKLINE != 0 {
1618 let proto = ci_lua_proto(&ci, state);
1619 let oldpc = if state.old_pc() < proto.code.len() as u32 {
1620 state.old_pc() as i32
1621 } else {
1622 0
1623 };
1624 let npci = next_pc as i32 - 1;
1626
1627 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
1628 let newline = get_func_line(&proto, npci);
1629 state.call_hook_event(LUA_HOOKLINE, newline)?;
1631 }
1632 state.set_old_pc(npci as u32);
1633 }
1634
1635 if state.status() == lua_types::status::LuaStatus::Yield {
1636 if counthook {
1637 state.set_hook_count(1);
1638 }
1639 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
1640 return Err(LuaError::Yield);
1642 }
1643
1644 Ok(1)
1645}
1646
1647fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
1655 out.fill(0);
1656 let n = crate::object::chunk_id(&mut out[..], source);
1657 if n < out.len() {
1658 out[n] = 0;
1659 }
1660}
1661
1662fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
1666 crate::func::get_local_name(p, n, pc)
1667}
1668
1669fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
1671 get_local_name(&cl.proto, n, pc)
1672}
1673
1674fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
1688 match state.get_at(ci.func) {
1689 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
1690 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
1691 }
1692}
1693
1694