1#[allow(unused_imports)]
10use crate::prelude::*;
11use crate::state::{
12 CallInfo, GcRef, LuaClosure, LuaClosureLua, LuaProto, LuaState, LuaTable, LuaValue, CIST_FIN,
13 CIST_HOOKED, CIST_HOOKYIELD, CIST_TAIL, CIST_TRAN,
14};
15use crate::vm::InstructionExt;
16use lua_types::error::LuaError;
17use lua_types::opcode::Instruction;
18use lua_types::{CallInfoIdx, LuaString, StackIdx};
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(None, &msg, src.map(|s| &**s), line);
86 runtime_bytes(prefixed)
87}
88
89pub fn c_api_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
90 let ci_idx = state.current_ci_idx();
91 if let Some(parent_idx) = state.prev_ci(ci_idx) {
92 let parent_ci = state.get_ci(parent_idx).clone();
93 if parent_ci.is_lua() {
94 let proto = ci_lua_proto(&parent_ci, state);
95 let src = proto.source_string();
96 let line = get_current_line(&parent_ci, state);
97 let prefixed = add_info(None, &msg, src.map(|s| &**s), line);
98 return runtime_bytes(prefixed);
99 }
100 }
101 runtime_bytes(msg)
102}
103
104#[allow(dead_code)]
114fn find_func_in_table(
115 table: &LuaTable,
116 target: &LuaValue,
117 prefix: &[u8],
118 depth: u8,
119) -> 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) =
152 find_func_in_table(&**sub, target, &new_prefix, depth - 1)
153 {
154 return Some(name);
155 }
156 }
157 }
158 }
159 }
160 key = k;
161 }
162 None
163}
164
165#[allow(dead_code)]
173fn find_func_name_in_globals(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
174 let globals = state.global().globals.clone();
175 if let LuaValue::Table(globals_table) = globals {
176 find_func_in_table(&*globals_table, func_val, b"", 1)
177 } else {
178 None
179 }
180}
181
182fn find_func_name_in_loaded(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
189 let registry = state.global().l_registry.clone();
190 let loaded = match registry {
191 LuaValue::Table(ref reg_table) => reg_table.get_str_bytes(b"_LOADED"),
192 _ => return None,
193 };
194 let loaded_table = match loaded {
195 LuaValue::Table(t) => t,
196 _ => return None,
197 };
198 find_func_in_table(&*loaded_table, func_val, b"", 1)
199}
200
201pub fn arg_error_impl(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> LuaError {
205 let mut ar = LuaDebug::default();
206 if !get_stack(state, 0, &mut ar) {
207 let msg = format!(
208 "bad argument #{} ({})",
209 arg,
210 String::from_utf8_lossy(extramsg)
211 );
212 return c_api_runtime(state, msg.into_bytes());
213 }
214 get_info(state, b"n", &mut ar);
215 if ar.namewhat.as_deref() == Some(b"method") {
216 arg -= 1;
217 if arg == 0 {
218 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
219 let msg = format!(
220 "calling '{}' on bad self ({})",
221 String::from_utf8_lossy(&name),
222 String::from_utf8_lossy(extramsg)
223 );
224 return c_api_runtime(state, msg.into_bytes());
225 }
226 }
227 let fname = ar
228 .name
229 .clone()
230 .or_else(|| {
231 let ci_idx = ar.i_ci?;
232 let func_slot = state.get_ci(ci_idx).func;
233 let func_val = state.get_at(func_slot).clone();
234 let found = find_func_name_in_loaded(state, &func_val)?;
235 if found.starts_with(b"_G.") {
236 Some(found[3..].to_vec())
237 } else {
238 Some(found)
239 }
240 })
241 .unwrap_or_else(|| b"?".to_vec());
242 let msg = format!(
243 "bad argument #{} to '{}' ({})",
244 arg,
245 String::from_utf8_lossy(&fname),
246 String::from_utf8_lossy(extramsg)
247 );
248 c_api_runtime(state, msg.into_bytes())
249}
250
251pub struct LuaDebug {
262 pub event: i32,
263 pub name: Option<Vec<u8>>,
264 pub namewhat: Option<&'static [u8]>,
265 pub what: Option<&'static [u8]>,
266 pub source: Option<Vec<u8>>,
267 pub srclen: usize,
268 pub currentline: i32,
269 pub linedefined: i32,
270 pub lastlinedefined: i32,
271 pub nups: u8,
272 pub nparams: u8,
273 pub isvararg: bool,
274 pub istailcall: bool,
275 pub extraargs: u8,
276 pub ftransfer: u16,
277 pub ntransfer: u16,
278 pub short_src: [u8; LUA_IDSIZE],
279 pub i_ci: Option<CallInfoIdx>,
281}
282
283impl Default for LuaDebug {
284 fn default() -> Self {
285 LuaDebug {
286 event: 0,
287 name: None,
288 namewhat: None,
289 what: None,
290 source: None,
291 srclen: 0,
292 currentline: -1,
293 linedefined: -1,
294 lastlinedefined: -1,
295 nups: 0,
296 nparams: 0,
297 isvararg: false,
298 istailcall: false,
299 extraargs: 0,
300 ftransfer: 0,
301 ntransfer: 0,
302 short_src: [0u8; LUA_IDSIZE],
303 i_ci: None,
304 }
305 }
306}
307
308#[inline]
312fn is_lua_closure(cl: Option<&LuaClosure>) -> bool {
313 matches!(cl, Some(LuaClosure::Lua(_)))
314}
315
316fn current_pc(ci: &CallInfo) -> i32 {
331 debug_assert!(ci.is_lua());
332 ci.saved_pc().saturating_sub(1) as i32
335}
336
337fn get_baseline(f: &LuaProto, pc: i32, basepc: &mut i32) -> i32 {
345 if f.abslineinfo.is_empty() || pc < f.abslineinfo[0].pc {
346 *basepc = -1;
347 return f.linedefined;
348 }
349 let mut i = (pc as u32 / MAX_IWTH_ABS as u32).saturating_sub(1) as usize;
351 debug_assert!(
352 i < f.abslineinfo.len() && f.abslineinfo[i].pc <= pc,
353 "getbaseline: estimate is not a lower bound"
354 );
355 while i + 1 < f.abslineinfo.len() && pc >= f.abslineinfo[i + 1].pc {
356 i += 1;
357 }
358 *basepc = f.abslineinfo[i].pc;
359 f.abslineinfo[i].line
360}
361
362pub(crate) fn get_func_line(f: &LuaProto, pc: i32) -> i32 {
366 if f.lineinfo.is_empty() {
367 return -1;
368 }
369 let mut basepc: i32 = 0;
370 let mut baseline = get_baseline(f, pc, &mut basepc);
371 while basepc < pc {
374 basepc += 1;
375 debug_assert!(
376 f.lineinfo[basepc as usize] != ABS_LINE_INFO,
377 "get_func_line: hit ABSLINEINFO in incremental walk"
378 );
379 baseline += f.lineinfo[basepc as usize] as i32;
380 }
381 baseline
382}
383
384fn get_current_line(ci: &CallInfo, state: &LuaState) -> i32 {
387 let proto = ci_lua_proto(ci, state);
388 get_func_line(&proto, current_pc(ci))
389}
390
391pub(crate) fn arm_traps(state: &mut LuaState) {
403 set_traps(state);
404}
405
406fn set_traps(state: &mut LuaState) {
407 for ci in state.call_stack_mut().iter_mut() {
411 if ci.is_lua() {
412 ci.set_trap(true);
413 }
414 }
415}
416
417pub fn set_hook(
420 state: &mut LuaState,
421 func: Option<Box<dyn FnMut(&mut LuaState, &LuaDebug)>>,
422 mask: i32,
423 count: i32,
424) {
425 let (func, mask) = if func.is_none() || mask == 0 {
426 (None, 0i32)
427 } else {
428 (func, mask)
429 };
430 state.set_hook(func);
431 state.set_base_hook_count(count);
432 state.reset_hook_count();
434 state.set_hook_mask(mask as u8);
436 if mask != 0 {
437 set_traps(state);
438 }
439}
440
441pub fn get_hook_installed(state: &LuaState) -> bool {
448 state.hook().is_some()
449}
450
451pub fn get_hook_mask(state: &LuaState) -> i32 {
454 state.hook_mask() as i32
455}
456
457pub fn get_hook_count(state: &LuaState) -> i32 {
460 state.base_hook_count()
461}
462
463pub fn get_stack(state: &LuaState, level: i32, ar: &mut LuaDebug) -> bool {
470 if level < 0 {
471 return false;
472 }
473 let mut remaining = level;
474 let mut ci_idx = state.current_ci_idx();
475 loop {
476 if remaining == 0 {
477 break;
478 }
479 match state.prev_ci(ci_idx) {
480 Some(prev) => {
481 ci_idx = prev;
482 remaining -= 1;
483 }
484 None => {
485 return false;
486 }
487 }
488 }
489 if !state.is_base_ci(ci_idx) {
490 ar.i_ci = Some(ci_idx);
491 true
492 } else {
493 false
494 }
495}
496
497fn upval_name(p: &LuaProto, uv: usize) -> &[u8] {
502 debug_assert!(uv < p.upvalues.len(), "upval_name: index out of range");
505 p.upvalues[uv]
508 .name
509 .as_ref()
510 .map_or(b"?" as &[u8], |s| s.as_bytes())
511}
512
513fn find_vararg(state: &LuaState, ci: &CallInfo, n: i32) -> Option<(StackIdx, &'static [u8])> {
520 let proto = ci_lua_proto(ci, state);
521 if proto.is_vararg {
522 let nextra = ci.nextra_args();
523 if n >= -(nextra as i32) {
524 let pos = ci.func - (nextra + n + 1);
527 return Some((pos, b"(vararg)" as &[u8]));
528 }
529 }
530 None
531}
532
533pub(crate) fn find_local(
546 state: &LuaState,
547 ci_idx: CallInfoIdx,
548 n: i32,
549 pos: Option<&mut StackIdx>,
550) -> Option<Vec<u8>> {
551 let ci = state.get_ci(ci_idx);
552 let base = ci.func + 1;
553 let mut name: Option<Vec<u8>> = None;
554
555 if ci.is_lua() {
556 if n < 0 {
557 if let Some((vpos, vname)) = find_vararg(state, ci, n) {
558 if let Some(out_pos) = pos {
559 *out_pos = vpos;
560 }
561 return Some(vname.to_vec());
562 }
563 return None;
564 } else {
565 let proto = ci_lua_proto(ci, state);
566 let pc = current_pc(ci);
567 name = crate::func::get_local_name(&proto, n, pc).map(|s| s.to_vec());
568 }
569 }
570
571 if name.is_none() {
572 let limit: u32 = if ci_idx == state.current_ci_idx() {
573 state.top_idx().0
574 } else {
575 ci.next
576 .map(|next| state.get_ci(next).func.0)
577 .unwrap_or_else(|| state.top_idx().0)
578 };
579 if n > 0 && limit.saturating_sub(base.0) >= n as u32 {
580 name = Some(if ci.is_lua() {
581 b"(temporary)".to_vec()
582 } else {
583 b"(C temporary)".to_vec()
584 });
585 } else {
586 return None;
587 }
588 }
589
590 if let Some(out_pos) = pos {
591 *out_pos = base + (n - 1);
592 }
593 name
594}
595
596pub fn get_local(state: &mut LuaState, ar: Option<&LuaDebug>, n: i32) -> Option<Vec<u8>> {
601 if ar.is_none() {
602 let top_val = state.peek_top();
604 if !matches!(top_val, LuaValue::Function(LuaClosure::Lua(_))) {
605 return None;
606 }
607 let name_owned: Option<Vec<u8>> = {
610 let cl = match top_val {
611 LuaValue::Function(LuaClosure::Lua(ref cl)) => cl.clone(),
612 _ => unreachable!(),
613 };
614 get_local_name_from_closure(&cl, n, 0).map(|s| s.to_vec())
616 };
617 return name_owned;
618 }
619
620 let ar = ar.unwrap();
621 let ci_idx = ar.i_ci?;
622 let mut pos = StackIdx(0);
623 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
626
627 if name_owned.is_some() {
628 let val = state.get_at(pos).clone();
629 state.push(val);
630 }
631 name_owned
632}
633
634pub fn set_local(state: &mut LuaState, ar: &LuaDebug, n: i32) -> Option<Vec<u8>> {
638 let ci_idx = ar.i_ci?;
639 let mut pos = StackIdx(0);
640 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
642 if name_owned.is_some() {
643 let val = state.get_at(state.top_idx() - 1).clone();
644 state.set_at(pos, val);
645 state.pop_n(1);
646 }
647 name_owned
648}
649
650fn func_info(ar: &mut LuaDebug, cl: Option<&LuaClosure>) {
655 if !is_lua_closure(cl) {
656 ar.source = Some(b"=[C]".to_vec());
658 ar.srclen = b"=[C]".len();
659 ar.linedefined = -1;
660 ar.lastlinedefined = -1;
661 ar.what = Some(b"C");
662 } else {
663 let lua_cl = match cl {
664 Some(LuaClosure::Lua(cl)) => cl,
665 _ => unreachable!(),
666 };
667 let proto: &LuaProto = &lua_cl.proto;
669 if let Some(src) = proto.source_string() {
671 ar.source = Some(src.as_bytes().to_vec());
672 ar.srclen = src.as_bytes().len();
673 } else {
674 ar.source = Some(b"=?".to_vec());
675 ar.srclen = b"=?".len();
676 }
677 ar.linedefined = proto.linedefined;
678 ar.lastlinedefined = proto.lastlinedefined;
679 ar.what = Some(if ar.linedefined == 0 { b"main" } else { b"Lua" });
680 }
681 chunk_id(
683 &mut ar.short_src,
684 ar.source.as_deref().unwrap_or(b"?"),
685 ar.srclen,
686 );
687}
688
689fn next_line(p: &LuaProto, currentline: i32, pc: usize) -> i32 {
693 if p.lineinfo.get(pc).copied() != Some(ABS_LINE_INFO) {
695 currentline + p.lineinfo[pc] as i32
696 } else {
697 get_func_line(p, pc as i32)
698 }
699}
700
701fn collect_valid_lines(state: &mut LuaState, cl: Option<&LuaClosure>) -> Result<(), LuaError> {
705 if !is_lua_closure(cl) {
706 state.push(LuaValue::Nil);
708 return Ok(());
709 }
710 let lua_cl = match cl {
711 Some(LuaClosure::Lua(cl)) => cl.clone(),
712 _ => unreachable!(),
713 };
714 let proto: GcRef<LuaProto> = lua_cl.proto.clone();
716 let p: &LuaProto = &proto;
717
718 let mut currentline = p.linedefined;
719
720 let t = state.new_table();
722 state.push(LuaValue::Table(t.clone()));
724
725 if !p.lineinfo.is_empty() {
726 let v = LuaValue::Bool(true);
728
729 let start_i = if !p.is_vararg {
730 0usize
731 } else {
732 debug_assert!(
734 p.code.first().map(|i| i.is_vararg_prep()).unwrap_or(false),
735 "collect_valid_lines: first instruction of vararg should be OP_VARARGPREP"
736 );
737 currentline = next_line(p, currentline, 0);
738 1usize
739 };
740
741 for i in start_i..p.lineinfo.len() {
743 currentline = next_line(p, currentline, i);
744 t.raw_set_int(state, currentline as i64, v.clone())?;
746 }
747 }
748 Ok(())
749}
750
751fn get_func_name<'a>(
757 state: &'a LuaState,
758 ci: Option<&CallInfo>,
759 name: &mut Option<Vec<u8>>,
760) -> Option<&'static [u8]> {
761 let ci = ci?;
764 if ci.callstatus & CIST_TAIL != 0 {
765 return None;
766 }
767 let prev_idx = ci.previous?;
770 let prev_ci = state.get_ci(prev_idx).clone();
771 funcname_from_call(state, &prev_ci, name)
772}
773
774fn aux_get_info(
777 state: &LuaState,
778 what: &[u8],
779 ar: &mut LuaDebug,
780 cl: Option<&LuaClosure>,
781 ci: Option<&CallInfo>,
782) -> bool {
783 let mut status = true;
784 for &ch in what {
785 match ch {
786 b'S' => {
787 func_info(ar, cl);
788 }
789 b'l' => {
790 ar.currentline = match ci {
791 Some(ci) if ci.is_lua() => get_current_line(ci, state),
792 _ => -1,
793 };
794 }
795 b'u' => {
796 ar.nups = cl.map_or(0, |c| c.nupvalues() as u8);
797 match cl {
798 Some(LuaClosure::Lua(lua_cl)) => {
799 ar.isvararg = lua_cl.proto.is_vararg;
801 ar.nparams = lua_cl.proto.numparams;
802 }
803 _ => {
804 ar.isvararg = true;
805 ar.nparams = 0;
806 }
807 }
808 }
809 b't' => {
810 if let Some(ci) = ci {
811 ar.istailcall = ci.callstatus & CIST_TAIL != 0;
812 ar.extraargs = ci.call_metamethods;
813 } else {
814 ar.istailcall = false;
815 ar.extraargs = 0;
816 }
817 }
818 b'n' => {
819 let mut name: Option<Vec<u8>> = None;
820 ar.namewhat = get_func_name(state, ci, &mut name);
821 if ar.namewhat.is_none() {
822 ar.namewhat = Some(b"");
823 ar.name = None;
824 } else {
825 ar.name = name;
826 }
827 }
828 b'r' => match ci {
830 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
831 ar.ftransfer = ci.transfer_ftransfer();
833 ar.ntransfer = ci.transfer_ntransfer();
834 }
835 _ => {
836 ar.ftransfer = 0;
837 ar.ntransfer = 0;
838 }
839 },
840 b'L' | b'f' => {}
841 _ => {
842 status = false;
843 }
844 }
845 }
846 status
847}
848
849pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
852 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
853 let func_val = state.peek_at(state.top_idx() - 1).clone();
854 state.pop_n(1);
855 debug_assert!(
856 matches!(func_val, LuaValue::Function(_)),
857 "get_info: function expected"
858 );
859 let cl = match &func_val {
860 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
861 LuaValue::Function(c) => c.clone(),
862 _ => unreachable!(),
863 }),
864 _ => None,
865 };
866 (cl, None, func_val, &what[1..])
867 } else {
868 let ci_idx = match ar.i_ci {
869 Some(i) => i,
870 None => return false,
871 };
872 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
873 debug_assert!(
874 matches!(func_val, LuaValue::Function(_)),
875 "get_info: non-function at ci->func"
876 );
877 let cl = match &func_val {
878 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
879 LuaValue::Function(c) => c.clone(),
880 _ => unreachable!(),
881 }),
882 _ => None,
883 };
884 (cl, Some(ci_idx), func_val, what)
885 };
886
887 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
888 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
889
890 if what.contains(&b'f') {
891 state.push(func_val);
892 }
893 if what.contains(&b'L') {
894 let _ = collect_valid_lines(state, cl.as_ref());
896 }
897 status
898}
899
900#[inline]
906fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
907 if pc < jmptarget {
908 -1
909 } else {
910 pc
911 }
912}
913
914fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
918 let mut setreg: i32 = -1;
919 let mut jmptarget: i32 = 0;
920
921 let effective_lastpc = if p
924 .code
925 .get(lastpc as usize)
926 .map_or(false, |i| i.is_mm_mode())
927 {
928 lastpc - 1
929 } else {
930 lastpc
931 };
932
933 for pc in 0..effective_lastpc {
934 let instr = p.code[pc as usize];
935 let op = instr.opcode();
936 let a = instr.arg_a() as i32;
937
938 let change = match op {
939 OpCode::LoadNil => {
940 let b = instr.arg_b() as i32;
941 a <= reg && reg <= a + b
942 }
943 OpCode::TForCall => reg >= a + 2,
944 OpCode::Call | OpCode::TailCall => reg >= a,
945 OpCode::Jmp => {
946 let b = instr.arg_s_j();
947 let dest = pc + 1 + b;
948 if dest <= effective_lastpc && dest > jmptarget {
949 jmptarget = dest;
950 }
951 false
952 }
953 _ => {
954 instr.test_a_mode() && reg == a
957 }
958 };
959
960 if change {
961 setreg = filter_pc(pc, jmptarget);
962 }
963 }
964 setreg
965}
966
967fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
972 match p.k.get(index) {
975 Some(LuaValue::Str(s)) => {
976 *name = s.as_bytes();
978 Some(b"constant")
979 }
980 _ => {
981 *name = b"?";
982 None
983 }
984 }
985}
986
987fn basic_get_obj_name<'a>(
991 p: &'a LuaProto,
992 ppc: &mut i32,
993 reg: i32,
994 name: &mut &'a [u8],
995) -> Option<&'static [u8]> {
996 let pc = *ppc;
997 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
999 *name = local_name;
1000 return Some(b"local");
1001 }
1002
1003 *ppc = find_set_reg(p, pc, reg);
1004 let pc = *ppc;
1005
1006 if pc == -1 {
1007 return None;
1008 }
1009
1010 let instr = p.code[pc as usize];
1011 let op = instr.opcode();
1012 match op {
1013 OpCode::Move => {
1014 let b = instr.arg_b() as i32;
1015 if b < instr.arg_a() as i32 {
1016 return basic_get_obj_name(p, ppc, b, name);
1017 }
1018 }
1019 OpCode::GetUpVal => {
1020 *name = upval_name(p, instr.arg_b() as usize);
1021 return Some(b"upvalue");
1022 }
1023 OpCode::LoadK => {
1024 return kname(p, instr.arg_bx() as usize, name);
1025 }
1026 OpCode::LoadKx => {
1027 let next = p.code[(pc + 1) as usize];
1028 return kname(p, next.arg_ax() as usize, name);
1029 }
1030 _ => {}
1031 }
1032 None
1033}
1034
1035fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
1039 let mut pc = pc;
1040 let what = basic_get_obj_name(p, &mut pc, c, name);
1042 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1043 *name = b"?";
1044 }
1045}
1046
1047fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1050 let c = instr.arg_c() as i32;
1051 if instr.arg_k() != 0 {
1053 kname(p, c as usize, name);
1054 } else {
1055 rname(p, pc, c, name);
1056 }
1057}
1058
1059fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1063 let t = instr.arg_b() as usize;
1064 let mut name: &[u8] = b"?";
1065 if isup {
1066 name = upval_name(p, t);
1067 } else {
1068 let mut pc = pc;
1069 let what = basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1070 if !matches!(what, Some(kind) if kind == b"local" || kind == b"upvalue") {
1071 name = b"?";
1072 }
1073 }
1074 if name == LUA_ENV {
1075 b"global"
1076 } else {
1077 b"field"
1078 }
1079}
1080
1081fn get_obj_name<'a>(
1085 p: &'a LuaProto,
1086 lastpc: i32,
1087 reg: i32,
1088 name: &mut &'a [u8],
1089) -> Option<&'static [u8]> {
1090 let mut lastpc = lastpc;
1091 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1092 if kind.is_some() {
1093 return kind;
1094 }
1095
1096 if lastpc == -1 {
1097 return None;
1098 }
1099
1100 let instr = p.code[lastpc as usize];
1101 let op = instr.opcode();
1102 match op {
1103 OpCode::GetTabUp => {
1104 let k = instr.arg_c() as usize;
1105 kname(p, k, name);
1106 Some(is_env(p, lastpc, instr, true))
1107 }
1108 OpCode::GetTable => {
1109 let k = instr.arg_c() as i32;
1110 rname(p, lastpc, k, name);
1111 Some(is_env(p, lastpc, instr, false))
1112 }
1113 OpCode::GetI => {
1114 *name = b"integer index";
1115 Some(b"field")
1116 }
1117 OpCode::GetField => {
1118 let k = instr.arg_c() as usize;
1119 kname(p, k, name);
1120 Some(is_env(p, lastpc, instr, false))
1121 }
1122 OpCode::Self_ => {
1123 rkname(p, lastpc, instr, name);
1124 Some(b"method")
1125 }
1126 _ => None,
1127 }
1128}
1129
1130fn funcname_from_code<'a>(
1137 state: &LuaState,
1138 p: &'a LuaProto,
1139 pc: i32,
1140 name: &mut Option<Vec<u8>>,
1141) -> Option<&'static [u8]> {
1142 let instr = p.code[pc as usize];
1143 let op = instr.opcode();
1144
1145 match op {
1146 OpCode::Call | OpCode::TailCall => {
1147 let mut name_bytes: &[u8] = b"?";
1148 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1149 *name = Some(name_bytes.to_vec());
1150 kind
1151 }
1152 OpCode::TForCall => {
1153 *name = Some(b"for iterator".to_vec());
1154 Some(b"for iterator")
1155 }
1156 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1158 get_tm_name(state, TagMethod::Index, name)
1159 }
1160 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1161 get_tm_name(state, TagMethod::NewIndex, name)
1162 }
1163 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1164 let tm_idx = instr.arg_c() as u8;
1167 let tm = TagMethod::from_u8(tm_idx);
1168 get_tm_name(state, tm, name)
1169 }
1170 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1171 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1172 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1173 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1174 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1175 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1176 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1177 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1178 _ => None,
1179 }
1180}
1181
1182fn get_tm_name(
1188 state: &LuaState,
1189 tm: TagMethod,
1190 name: &mut Option<Vec<u8>>,
1191) -> Option<&'static [u8]> {
1192 let raw_bytes: Vec<u8> = state
1196 .global()
1197 .tm_name(tm)
1198 .map(|s| s.as_bytes().to_vec())
1199 .unwrap_or_default();
1200 let stripped = raw_bytes.strip_prefix(b"__").unwrap_or(&raw_bytes).to_vec();
1201 *name = Some(stripped);
1202 Some(b"metamethod")
1203}
1204
1205fn funcname_from_call<'a>(
1208 state: &'a LuaState,
1209 ci: &CallInfo,
1210 name: &mut Option<Vec<u8>>,
1211) -> Option<&'static [u8]> {
1212 if ci.callstatus & CIST_HOOKED != 0 {
1213 *name = Some(b"?".to_vec());
1214 return Some(b"hook");
1215 }
1216 if ci.callstatus & CIST_FIN != 0 {
1217 *name = Some(b"__gc".to_vec());
1218 return Some(b"metamethod");
1219 }
1220 if ci.is_lua() {
1221 let proto = ci_lua_proto(ci, state);
1222 return funcname_from_code(state, &proto, current_pc(ci), name);
1223 }
1224 None
1225}
1226
1227fn in_stack(ci: &CallInfo, val_idx: StackIdx) -> i32 {
1238 let base = StackIdx(ci.func.0 + 1);
1239 let ci_top = ci.top;
1242 let mut pos = 0i32;
1243 let mut cur = base;
1244 while cur.0 < ci_top.0 {
1245 if cur == val_idx {
1246 return pos;
1247 }
1248 cur = StackIdx(cur.0 + 1);
1249 pos += 1;
1250 }
1251 -1
1252}
1253
1254fn get_upval_name<'a>(
1263 ci: &CallInfo,
1264 val_idx: StackIdx,
1265 name: &mut &'a [u8],
1266 state: &'a LuaState,
1267) -> Option<&'static [u8]> {
1268 let proto = ci_lua_proto(ci, state);
1269 let lua_cl = match state.get_at(ci.func) {
1272 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1273 _ => return None,
1274 };
1275 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1276 let upval = upval_slot.get();
1277 let state = upval.slot().clone();
1278 if let lua_types::UpValState::Open { idx, .. } = state {
1279 if idx == val_idx {
1280 let _ = upval_name(&proto, i);
1283 *name = b"upvalue";
1284 return Some(b"upvalue");
1285 }
1286 }
1287 }
1288 None
1289}
1290
1291fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1296 match (kind, name) {
1297 (Some(k), Some(n)) => {
1298 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1299 out.extend_from_slice(b" (");
1300 out.extend_from_slice(k);
1301 out.extend_from_slice(b" '");
1302 out.extend_from_slice(n);
1303 out.extend_from_slice(b"')");
1304 out
1305 }
1306 _ => Vec::new(),
1307 }
1308}
1309
1310fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1314 let ci_idx = state.current_ci_idx();
1315 let ci = state.get_ci(ci_idx).clone();
1316 let mut kind: Option<&[u8]> = None;
1317 let mut name_owned: Vec<u8> = b"?".to_vec();
1318
1319 if ci.is_lua() {
1320 let mut up_name: &[u8] = b"?";
1321 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1322 if kind.is_some() {
1323 name_owned = up_name.to_vec();
1324 } else {
1325 let reg = in_stack(&ci, val_idx);
1326 if reg >= 0 {
1327 let proto = ci_lua_proto(&ci, state);
1328 let mut nref: &[u8] = b"?";
1329 let pc = current_pc(&ci);
1330 let k = get_obj_name(&proto, pc, reg, &mut nref);
1331 kind = k;
1332 if kind.is_some() {
1333 name_owned = nref.to_vec();
1334 }
1335 }
1336 }
1337 }
1338 format_var_info(
1339 kind,
1340 if kind.is_some() {
1341 Some(&name_owned)
1342 } else {
1343 None
1344 },
1345 )
1346}
1347
1348fn typeerror_inner(state: &LuaState, val: &LuaValue, op: &[u8], extra: &[u8]) -> LuaError {
1353 let t = state.obj_type_name(val);
1354 let mut msg = Vec::new();
1355 msg.extend_from_slice(b"attempt to ");
1356 msg.extend_from_slice(op);
1357 msg.extend_from_slice(b" a ");
1358 msg.extend_from_slice(&t);
1359 msg.extend_from_slice(b" value");
1360 msg.extend_from_slice(extra);
1361 prefixed_runtime(state, msg)
1362}
1363
1364pub(crate) fn type_error(
1368 state: &LuaState,
1369 val: &LuaValue,
1370 val_idx: StackIdx,
1371 op: &[u8],
1372) -> LuaError {
1373 let extra = var_info(state, val_idx);
1374 typeerror_inner(state, val, op, &extra)
1375}
1376
1377pub(crate) fn type_error_with_hint(
1383 state: &LuaState,
1384 val: &LuaValue,
1385 op: &[u8],
1386 kind: &[u8],
1387 name: &[u8],
1388) -> LuaError {
1389 let extra = format_var_info(Some(kind), Some(name));
1390 let t = obj_type_name_static(val);
1391 let mut msg = Vec::new();
1392 msg.extend_from_slice(b"attempt to ");
1393 msg.extend_from_slice(op);
1394 msg.extend_from_slice(b" a ");
1395 msg.extend_from_slice(t);
1396 msg.extend_from_slice(b" value");
1397 msg.extend_from_slice(&extra);
1398 prefixed_runtime(state, msg)
1399}
1400
1401fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1404 match val {
1405 LuaValue::Nil => b"nil",
1406 LuaValue::Bool(_) => b"boolean",
1407 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1408 LuaValue::Str(_) => b"string",
1409 LuaValue::Table(_) => b"table",
1410 LuaValue::Function(_) => b"function",
1411 LuaValue::UserData(_) => b"userdata",
1412 LuaValue::LightUserData(_) => b"light userdata",
1413 LuaValue::Thread(_) => b"thread",
1414 }
1415}
1416
1417pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1421 let ci_idx = state.current_ci_idx();
1422 let ci = state.get_ci(ci_idx).clone();
1423 let mut name: Option<Vec<u8>> = None;
1424 let kind = funcname_from_call(state, &ci, &mut name);
1425 let extra = if kind.is_some() {
1426 format_var_info(kind, name.as_deref())
1427 } else {
1428 var_info(state, val_idx)
1429 };
1430 typeerror_inner(state, val, b"call", &extra)
1431}
1432
1433pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1436 if matches!(
1440 state.global().lua_version,
1441 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1442 ) {
1443 let mut msg = Vec::new();
1444 msg.extend_from_slice(b"'for' ");
1445 msg.extend_from_slice(what);
1446 msg.extend_from_slice(b" must be a number");
1447 return prefixed_runtime(state, msg);
1448 }
1449 let t = crate::tagmethods::obj_type_name(state, val)
1450 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1451 let mut msg = Vec::new();
1452 msg.extend_from_slice(b"bad 'for' ");
1453 msg.extend_from_slice(what);
1454 msg.extend_from_slice(b" (number expected, got ");
1455 msg.extend_from_slice(&t);
1456 msg.push(b')');
1457 prefixed_runtime(state, msg)
1458}
1459
1460pub(crate) fn op_int_error(
1464 state: &LuaState,
1465 p1: &LuaValue,
1466 p1_idx: StackIdx,
1467 p2: &LuaValue,
1468 p2_idx: StackIdx,
1469 msg: &[u8],
1470) -> LuaError {
1471 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1473 (p1, p1_idx)
1474 } else {
1475 (p2, p2_idx)
1476 };
1477 type_error(state, bad_val, bad_idx, msg)
1478}
1479
1480pub(crate) fn to_int_error(
1486 state: &LuaState,
1487 p1: &LuaValue,
1488 p1_idx: Option<StackIdx>,
1489 _p2: &LuaValue,
1490 p2_idx: Option<StackIdx>,
1491) -> LuaError {
1492 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1493 p1_idx
1494 } else {
1495 p2_idx
1496 };
1497 let extra = match bad_idx {
1498 Some(idx) => var_info(state, idx),
1499 None => Vec::new(),
1500 };
1501 let mut msg = Vec::new();
1502 msg.extend_from_slice(b"number");
1503 msg.extend_from_slice(&extra);
1504 msg.extend_from_slice(b" has no integer representation");
1505 prefixed_runtime(state, msg)
1506}
1507
1508pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1511 let t1 = state.obj_type_name(p1);
1513 let t2 = state.obj_type_name(p2);
1514 let msg = if t1 == t2 {
1516 let mut m = Vec::new();
1517 m.extend_from_slice(b"attempt to compare two ");
1518 m.extend_from_slice(&t1);
1519 m.extend_from_slice(b" values");
1520 m
1521 } else {
1522 let mut m = Vec::new();
1523 m.extend_from_slice(b"attempt to compare ");
1524 m.extend_from_slice(&t1);
1525 m.extend_from_slice(b" with ");
1526 m.extend_from_slice(&t2);
1527 m
1528 };
1529 prefixed_runtime(state, msg)
1530}
1531
1532pub(crate) fn add_info(
1541 _state: Option<&mut LuaState>,
1542 msg: &[u8],
1543 src: Option<&LuaString>,
1544 line: i32,
1545) -> Vec<u8> {
1546 let mut buff = [0u8; LUA_IDSIZE];
1548 if let Some(src) = src {
1549 chunk_id(&mut buff, src.as_bytes(), src.len());
1552 } else {
1553 let mut out = Vec::with_capacity(5 + msg.len());
1554 out.extend_from_slice(b"?:?: ");
1555 out.extend_from_slice(msg);
1556 return out;
1557 }
1558 let src_part = buff
1561 .iter()
1562 .position(|&b| b == 0)
1563 .map_or(&buff[..], |n| &buff[..n]);
1564 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1565 out.extend_from_slice(src_part);
1566 out.push(b':');
1567 let line_str = line.to_string();
1569 out.extend_from_slice(line_str.as_bytes());
1570 out.extend_from_slice(b": ");
1571 out.extend_from_slice(msg);
1572 out
1573}
1574
1575fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1580 if p.lineinfo.is_empty() {
1581 return false;
1582 }
1583
1584 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1585 let mut delta: i32 = 0;
1586 let mut pc = oldpc;
1587 loop {
1588 pc += 1;
1589 if pc as usize >= p.lineinfo.len() {
1590 break;
1591 }
1592 let lineinfo = p.lineinfo[pc as usize];
1593 if lineinfo == ABS_LINE_INFO {
1594 break;
1595 }
1596 delta += lineinfo as i32;
1597 if pc == newpc {
1598 return delta != 0;
1599 }
1600 }
1601 }
1602 get_func_line(p, oldpc) != get_func_line(p, newpc)
1603}
1604
1605pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1611 let ci_idx = state.current_ci_idx();
1612 let ci = state.get_ci(ci_idx).clone();
1613 state.get_ci_mut(ci_idx).set_trap(true);
1614 let proto = ci_lua_proto(&ci, state);
1615
1616 if ci.saved_pc() == 0 {
1617 if proto.is_vararg {
1618 return Ok(0);
1619 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1620 state.hook_call(ci_idx)?;
1622 }
1623 }
1624 Ok(1)
1625}
1626
1627pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
1637 let ci_idx = state.current_ci_idx();
1638 let ci = state.get_ci(ci_idx).clone();
1639
1640 let mask = state.hook_mask();
1641
1642 if !state.allowhook {
1643 return Ok(1);
1644 }
1645
1646 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
1647 state.get_ci_mut(ci_idx).set_trap(false);
1648 return Ok(0);
1649 }
1650
1651 let next_pc = pc + 1;
1652 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
1653
1654 let counthook = if mask & LUA_MASKCOUNT != 0 {
1655 let hc = state.hook_count() - 1;
1656 state.set_hook_count(hc);
1657 hc == 0
1658 } else {
1659 false
1660 };
1661
1662 if counthook {
1663 state.reset_hook_count();
1664 } else if mask & LUA_MASKLINE == 0 {
1665 return Ok(1);
1666 }
1667
1668 if counthook {
1673 if let Some(err) = state.sandbox_charge_interval() {
1674 return Err(err);
1675 }
1676 }
1677
1678 if ci.callstatus & CIST_HOOKYIELD != 0 {
1679 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
1680 return Ok(1);
1681 }
1682
1683 if state.ci_lua_closure(ci_idx).is_none() {
1684 return Ok(1);
1685 }
1686
1687 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
1690 if !cur_instr.is_in_top() {
1691 let ci_top = state.get_ci(ci_idx).top;
1692 state.set_top(ci_top);
1693 }
1694
1695 if counthook {
1696 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
1698 }
1699
1700 if mask & LUA_MASKLINE != 0 {
1701 let proto = ci_lua_proto(&ci, state);
1702 let oldpc = if state.old_pc() < proto.code.len() as u32 {
1703 state.old_pc() as i32
1704 } else {
1705 0
1706 };
1707 let npci = next_pc as i32 - 1;
1709
1710 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
1711 let newline = get_func_line(&proto, npci);
1712 state.call_hook_event(LUA_HOOKLINE, newline)?;
1714 }
1715 state.set_old_pc(npci as u32);
1716 }
1717
1718 if state.status() == lua_types::status::LuaStatus::Yield {
1719 if counthook {
1720 state.set_hook_count(1);
1721 }
1722 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
1723 return Err(LuaError::Yield);
1725 }
1726
1727 Ok(1)
1728}
1729
1730fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
1738 out.fill(0);
1739 let n = crate::object::chunk_id(&mut out[..], source);
1740 if n < out.len() {
1741 out[n] = 0;
1742 }
1743}
1744
1745fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
1749 crate::func::get_local_name(p, n, pc)
1750}
1751
1752fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
1754 get_local_name(&cl.proto, n, pc)
1755}
1756
1757fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
1771 match state.get_at(ci.func) {
1772 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
1773 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
1774 }
1775}
1776
1777