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 unknown_line_as_question =
86 src.is_none() && state.global().lua_version == lua_types::LuaVersion::V55;
87 let prefixed = add_info(
88 None,
89 &msg,
90 src.map(|s| &**s),
91 line,
92 unknown_line_as_question,
93 );
94 runtime_bytes(prefixed)
95}
96
97pub fn c_api_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
98 let ci_idx = state.current_ci_idx();
99 if let Some(parent_idx) = state.prev_ci(ci_idx) {
100 let parent_ci = state.get_ci(parent_idx).clone();
101 if parent_ci.is_lua() {
102 let proto = ci_lua_proto(&parent_ci, state);
103 let src = proto.source_string();
104 let line = get_current_line(&parent_ci, state);
105 let unknown_line_as_question =
106 src.is_none() && state.global().lua_version == lua_types::LuaVersion::V55;
107 let prefixed = add_info(
108 None,
109 &msg,
110 src.map(|s| &**s),
111 line,
112 unknown_line_as_question,
113 );
114 return runtime_bytes(prefixed);
115 }
116 }
117 runtime_bytes(msg)
118}
119
120#[allow(dead_code)]
130fn find_func_in_table(
131 table: &LuaTable,
132 target: &LuaValue,
133 prefix: &[u8],
134 depth: u8,
135) -> Option<Vec<u8>> {
136 let mut key = LuaValue::Nil;
137 loop {
138 let (k, v) = match table.next_pair(&key) {
139 Some(pair) => pair,
140 None => break,
141 };
142 if !matches!(v, LuaValue::Nil) {
143 let key_bytes: Option<Vec<u8>> = match &k {
144 LuaValue::Str(s) => Some(s.as_bytes().to_vec()),
145 _ => None,
146 };
147 if let Some(kb) = key_bytes {
148 if &v == target {
149 if prefix.is_empty() {
150 return Some(kb);
151 }
152 let mut result = prefix.to_vec();
153 result.push(b'.');
154 result.extend_from_slice(&kb);
155 return Some(result);
156 }
157 if depth > 0 {
158 if let LuaValue::Table(sub) = &v {
159 let new_prefix = if prefix.is_empty() {
160 kb.clone()
161 } else {
162 let mut p = prefix.to_vec();
163 p.push(b'.');
164 p.extend_from_slice(&kb);
165 p
166 };
167 if let Some(name) =
168 find_func_in_table(&**sub, target, &new_prefix, depth - 1)
169 {
170 return Some(name);
171 }
172 }
173 }
174 }
175 }
176 key = k;
177 }
178 None
179}
180
181#[allow(dead_code)]
189fn find_func_name_in_globals(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
190 let globals = state.global().globals.clone();
191 if let LuaValue::Table(globals_table) = globals {
192 find_func_in_table(&*globals_table, func_val, b"", 1)
193 } else {
194 None
195 }
196}
197
198fn find_func_name_in_loaded(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
205 let registry = state.global().l_registry.clone();
206 let loaded = match registry {
207 LuaValue::Table(ref reg_table) => reg_table.get_str_bytes(b"_LOADED"),
208 _ => return None,
209 };
210 let loaded_table = match loaded {
211 LuaValue::Table(t) => t,
212 _ => return None,
213 };
214 find_func_in_table(&*loaded_table, func_val, b"", 1)
215}
216
217pub fn arg_error_impl(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> LuaError {
221 let mut ar = LuaDebug::default();
222 if !get_stack(state, 0, &mut ar) {
223 let msg = format!(
224 "bad argument #{} ({})",
225 arg,
226 String::from_utf8_lossy(extramsg)
227 );
228 return c_api_runtime(state, msg.into_bytes());
229 }
230 get_info(state, b"n", &mut ar);
231 if ar.namewhat.as_deref() == Some(b"method") {
232 arg -= 1;
233 if arg == 0 {
234 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
235 let msg = format!(
236 "calling '{}' on bad self ({})",
237 String::from_utf8_lossy(&name),
238 String::from_utf8_lossy(extramsg)
239 );
240 return c_api_runtime(state, msg.into_bytes());
241 }
242 }
243 let fname = ar
244 .name
245 .clone()
246 .or_else(|| {
247 let ci_idx = ar.i_ci?;
248 let func_slot = state.get_ci(ci_idx).func;
249 let func_val = state.get_at(func_slot).clone();
250 let found = find_func_name_in_loaded(state, &func_val)?;
251 if found.starts_with(b"_G.") {
252 Some(found[3..].to_vec())
253 } else {
254 Some(found)
255 }
256 })
257 .unwrap_or_else(|| b"?".to_vec());
258 let msg = format!(
259 "bad argument #{} to '{}' ({})",
260 arg,
261 String::from_utf8_lossy(&fname),
262 String::from_utf8_lossy(extramsg)
263 );
264 c_api_runtime(state, msg.into_bytes())
265}
266
267pub struct LuaDebug {
278 pub event: i32,
279 pub name: Option<Vec<u8>>,
280 pub namewhat: Option<&'static [u8]>,
281 pub what: Option<&'static [u8]>,
282 pub source: Option<Vec<u8>>,
283 pub srclen: usize,
284 pub currentline: i32,
285 pub linedefined: i32,
286 pub lastlinedefined: i32,
287 pub nups: u8,
288 pub nparams: u8,
289 pub isvararg: bool,
290 pub istailcall: bool,
291 pub extraargs: u8,
292 pub ftransfer: u16,
293 pub ntransfer: u16,
294 pub short_src: [u8; LUA_IDSIZE],
295 pub i_ci: Option<CallInfoIdx>,
297}
298
299impl Default for LuaDebug {
300 fn default() -> Self {
301 LuaDebug {
302 event: 0,
303 name: None,
304 namewhat: None,
305 what: None,
306 source: None,
307 srclen: 0,
308 currentline: -1,
309 linedefined: -1,
310 lastlinedefined: -1,
311 nups: 0,
312 nparams: 0,
313 isvararg: false,
314 istailcall: false,
315 extraargs: 0,
316 ftransfer: 0,
317 ntransfer: 0,
318 short_src: [0u8; LUA_IDSIZE],
319 i_ci: None,
320 }
321 }
322}
323
324#[inline]
328fn is_lua_closure(cl: Option<&LuaClosure>) -> bool {
329 matches!(cl, Some(LuaClosure::Lua(_)))
330}
331
332fn current_pc(ci: &CallInfo) -> i32 {
347 debug_assert!(ci.is_lua());
348 ci.saved_pc().saturating_sub(1) as i32
351}
352
353fn get_baseline(f: &LuaProto, pc: i32, basepc: &mut i32) -> i32 {
361 if f.abslineinfo.is_empty() || pc < f.abslineinfo[0].pc {
362 *basepc = -1;
363 return f.linedefined;
364 }
365 let mut i = (pc as u32 / MAX_IWTH_ABS as u32).saturating_sub(1) as usize;
367 debug_assert!(
368 i < f.abslineinfo.len() && f.abslineinfo[i].pc <= pc,
369 "getbaseline: estimate is not a lower bound"
370 );
371 while i + 1 < f.abslineinfo.len() && pc >= f.abslineinfo[i + 1].pc {
372 i += 1;
373 }
374 *basepc = f.abslineinfo[i].pc;
375 f.abslineinfo[i].line
376}
377
378pub(crate) fn get_func_line(f: &LuaProto, pc: i32) -> i32 {
382 if f.lineinfo.is_empty() {
383 return -1;
384 }
385 let mut basepc: i32 = 0;
386 let mut baseline = get_baseline(f, pc, &mut basepc);
387 while basepc < pc {
390 basepc += 1;
391 debug_assert!(
392 f.lineinfo[basepc as usize] != ABS_LINE_INFO,
393 "get_func_line: hit ABSLINEINFO in incremental walk"
394 );
395 baseline += f.lineinfo[basepc as usize] as i32;
396 }
397 baseline
398}
399
400fn get_current_line(ci: &CallInfo, state: &LuaState) -> i32 {
403 let proto = ci_lua_proto(ci, state);
404 get_func_line(&proto, current_pc(ci))
405}
406
407pub(crate) fn arm_traps(state: &mut LuaState) {
419 set_traps(state);
420}
421
422fn set_traps(state: &mut LuaState) {
423 for ci in state.call_stack_mut().iter_mut() {
427 if ci.is_lua() {
428 ci.set_trap(true);
429 }
430 }
431}
432
433pub fn set_hook(
436 state: &mut LuaState,
437 func: Option<Box<dyn FnMut(&mut LuaState, &LuaDebug)>>,
438 mask: i32,
439 count: i32,
440) {
441 let (func, mask) = if func.is_none() || mask == 0 {
442 (None, 0i32)
443 } else {
444 (func, mask)
445 };
446 state.set_hook(func);
447 state.set_base_hook_count(count);
448 state.reset_hook_count();
450 state.set_hook_mask(mask as u8);
452 if mask != 0 {
453 set_traps(state);
454 }
455}
456
457pub fn get_hook_installed(state: &LuaState) -> bool {
464 state.hook().is_some()
465}
466
467pub fn get_hook_mask(state: &LuaState) -> i32 {
470 state.hook_mask() as i32
471}
472
473pub fn get_hook_count(state: &LuaState) -> i32 {
476 state.base_hook_count()
477}
478
479pub fn get_stack(state: &LuaState, level: i32, ar: &mut LuaDebug) -> bool {
486 if level < 0 {
487 return false;
488 }
489 let mut remaining = level;
490 let mut ci_idx = state.current_ci_idx();
491 loop {
492 if remaining == 0 {
493 break;
494 }
495 match state.prev_ci(ci_idx) {
496 Some(prev) => {
497 ci_idx = prev;
498 remaining -= 1;
499 }
500 None => {
501 return false;
502 }
503 }
504 }
505 if !state.is_base_ci(ci_idx) {
506 ar.i_ci = Some(ci_idx);
507 true
508 } else {
509 false
510 }
511}
512
513fn upval_name(p: &LuaProto, uv: usize) -> &[u8] {
518 debug_assert!(uv < p.upvalues.len(), "upval_name: index out of range");
521 p.upvalues[uv]
524 .name
525 .as_ref()
526 .map_or(b"?" as &[u8], |s| s.as_bytes())
527}
528
529fn find_vararg(state: &LuaState, ci: &CallInfo, n: i32) -> Option<(StackIdx, &'static [u8])> {
536 let proto = ci_lua_proto(ci, state);
537 if proto.is_vararg {
538 let nextra = ci.nextra_args();
539 if n >= -(nextra as i32) {
540 let pos = ci.func - (nextra + n + 1);
543 return Some((pos, b"(vararg)" as &[u8]));
544 }
545 }
546 None
547}
548
549pub(crate) fn find_local(
562 state: &LuaState,
563 ci_idx: CallInfoIdx,
564 n: i32,
565 pos: Option<&mut StackIdx>,
566) -> Option<Vec<u8>> {
567 let ci = state.get_ci(ci_idx);
568 let base = ci.func + 1;
569 let mut name: Option<Vec<u8>> = None;
570
571 if ci.is_lua() {
572 if n < 0 {
573 if let Some((vpos, vname)) = find_vararg(state, ci, n) {
574 if let Some(out_pos) = pos {
575 *out_pos = vpos;
576 }
577 return Some(vname.to_vec());
578 }
579 return None;
580 } else {
581 let proto = ci_lua_proto(ci, state);
582 let pc = current_pc(ci);
583 name = crate::func::get_local_name(&proto, n, pc).map(|s| s.to_vec());
584 }
585 }
586
587 if name.is_none() {
588 let limit: u32 = if ci_idx == state.current_ci_idx() {
589 state.top_idx().0
590 } else {
591 ci.next
592 .map(|next| state.get_ci(next).func.0)
593 .unwrap_or_else(|| state.top_idx().0)
594 };
595 if n > 0 && limit.saturating_sub(base.0) >= n as u32 {
596 name = Some(if ci.is_lua() {
597 b"(temporary)".to_vec()
598 } else {
599 b"(C temporary)".to_vec()
600 });
601 } else {
602 return None;
603 }
604 }
605
606 if let Some(out_pos) = pos {
607 *out_pos = base + (n - 1);
608 }
609 name
610}
611
612pub fn get_local(state: &mut LuaState, ar: Option<&LuaDebug>, n: i32) -> Option<Vec<u8>> {
617 if ar.is_none() {
618 let top_val = state.peek_top();
620 if !matches!(top_val, LuaValue::Function(LuaClosure::Lua(_))) {
621 return None;
622 }
623 let name_owned: Option<Vec<u8>> = {
626 let cl = match top_val {
627 LuaValue::Function(LuaClosure::Lua(ref cl)) => cl.clone(),
628 _ => unreachable!(),
629 };
630 get_local_name_from_closure(&cl, n, 0).map(|s| s.to_vec())
632 };
633 return name_owned;
634 }
635
636 let ar = ar.unwrap();
637 let ci_idx = ar.i_ci?;
638 let mut pos = StackIdx(0);
639 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
642
643 if name_owned.is_some() {
644 let val = state.get_at(pos).clone();
645 state.push(val);
646 }
647 name_owned
648}
649
650pub fn set_local(state: &mut LuaState, ar: &LuaDebug, n: i32) -> Option<Vec<u8>> {
654 let ci_idx = ar.i_ci?;
655 let mut pos = StackIdx(0);
656 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
658 if name_owned.is_some() {
659 let val = state.get_at(state.top_idx() - 1).clone();
660 state.set_at(pos, val);
661 state.pop_n(1);
662 }
663 name_owned
664}
665
666fn func_info(ar: &mut LuaDebug, cl: Option<&LuaClosure>) {
671 if !is_lua_closure(cl) {
672 ar.source = Some(b"=[C]".to_vec());
674 ar.srclen = b"=[C]".len();
675 ar.linedefined = -1;
676 ar.lastlinedefined = -1;
677 ar.what = Some(b"C");
678 } else {
679 let lua_cl = match cl {
680 Some(LuaClosure::Lua(cl)) => cl,
681 _ => unreachable!(),
682 };
683 let proto: &LuaProto = &lua_cl.proto;
685 if let Some(src) = proto.source_string() {
687 ar.source = Some(src.as_bytes().to_vec());
688 ar.srclen = src.as_bytes().len();
689 } else {
690 ar.source = Some(b"=?".to_vec());
691 ar.srclen = b"=?".len();
692 }
693 ar.linedefined = proto.linedefined;
694 ar.lastlinedefined = proto.lastlinedefined;
695 ar.what = Some(if ar.linedefined == 0 { b"main" } else { b"Lua" });
696 }
697 chunk_id(
699 &mut ar.short_src,
700 ar.source.as_deref().unwrap_or(b"?"),
701 ar.srclen,
702 );
703}
704
705fn next_line(p: &LuaProto, currentline: i32, pc: usize) -> i32 {
709 if p.lineinfo.get(pc).copied() != Some(ABS_LINE_INFO) {
711 currentline + p.lineinfo[pc] as i32
712 } else {
713 get_func_line(p, pc as i32)
714 }
715}
716
717fn collect_valid_lines(state: &mut LuaState, cl: Option<&LuaClosure>) -> Result<(), LuaError> {
721 if !is_lua_closure(cl) {
722 state.push(LuaValue::Nil);
724 return Ok(());
725 }
726 let lua_cl = match cl {
727 Some(LuaClosure::Lua(cl)) => cl.clone(),
728 _ => unreachable!(),
729 };
730 let proto: GcRef<LuaProto> = lua_cl.proto.clone();
732 let p: &LuaProto = &proto;
733
734 let mut currentline = p.linedefined;
735
736 let t = state.new_table();
738 state.push(LuaValue::Table(t.clone()));
740
741 if !p.lineinfo.is_empty() {
742 let v = LuaValue::Bool(true);
744
745 let start_i = if !p.is_vararg {
746 0usize
747 } else {
748 debug_assert!(
750 p.code.first().map(|i| i.is_vararg_prep()).unwrap_or(false),
751 "collect_valid_lines: first instruction of vararg should be OP_VARARGPREP"
752 );
753 currentline = next_line(p, currentline, 0);
754 1usize
755 };
756
757 for i in start_i..p.lineinfo.len() {
759 currentline = next_line(p, currentline, i);
760 t.raw_set_int(state, currentline as i64, v.clone())?;
762 }
763 }
764 Ok(())
765}
766
767fn get_func_name<'a>(
773 state: &'a LuaState,
774 ci: Option<&CallInfo>,
775 name: &mut Option<Vec<u8>>,
776) -> Option<&'static [u8]> {
777 let ci = ci?;
780 if ci.callstatus & CIST_TAIL != 0 {
781 return None;
782 }
783 let prev_idx = ci.previous?;
786 let prev_ci = state.get_ci(prev_idx).clone();
787 funcname_from_call(state, &prev_ci, name)
788}
789
790fn aux_get_info(
793 state: &LuaState,
794 what: &[u8],
795 ar: &mut LuaDebug,
796 cl: Option<&LuaClosure>,
797 ci: Option<&CallInfo>,
798) -> bool {
799 let mut status = true;
800 for &ch in what {
801 match ch {
802 b'S' => {
803 func_info(ar, cl);
804 }
805 b'l' => {
806 ar.currentline = match ci {
807 Some(ci) if ci.is_lua() => get_current_line(ci, state),
808 _ => -1,
809 };
810 }
811 b'u' => {
812 ar.nups = cl.map_or(0, |c| c.nupvalues() as u8);
813 match cl {
814 Some(LuaClosure::Lua(lua_cl)) => {
815 ar.isvararg = lua_cl.proto.is_vararg;
817 ar.nparams = lua_cl.proto.numparams;
818 }
819 _ => {
820 ar.isvararg = true;
821 ar.nparams = 0;
822 }
823 }
824 }
825 b't' => {
826 if let Some(ci) = ci {
827 ar.istailcall = ci.callstatus & CIST_TAIL != 0;
828 ar.extraargs = ci.call_metamethods;
829 } else {
830 ar.istailcall = false;
831 ar.extraargs = 0;
832 }
833 }
834 b'n' => {
835 let mut name: Option<Vec<u8>> = None;
836 ar.namewhat = get_func_name(state, ci, &mut name);
837 if ar.namewhat.is_none() {
838 ar.namewhat = Some(b"");
839 ar.name = None;
840 } else {
841 ar.name = name;
842 }
843 }
844 b'r' => match ci {
846 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
847 ar.ftransfer = ci.transfer_ftransfer();
849 ar.ntransfer = ci.transfer_ntransfer();
850 }
851 _ => {
852 ar.ftransfer = 0;
853 ar.ntransfer = 0;
854 }
855 },
856 b'L' | b'f' => {}
857 _ => {
858 status = false;
859 }
860 }
861 }
862 status
863}
864
865pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
868 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
869 let func_val = state.peek_at(state.top_idx() - 1).clone();
870 state.pop_n(1);
871 debug_assert!(
872 matches!(func_val, LuaValue::Function(_)),
873 "get_info: function expected"
874 );
875 let cl = match &func_val {
876 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
877 LuaValue::Function(c) => c.clone(),
878 _ => unreachable!(),
879 }),
880 _ => None,
881 };
882 (cl, None, func_val, &what[1..])
883 } else {
884 let ci_idx = match ar.i_ci {
885 Some(i) => i,
886 None => return false,
887 };
888 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
889 debug_assert!(
890 matches!(func_val, LuaValue::Function(_)),
891 "get_info: non-function at ci->func"
892 );
893 let cl = match &func_val {
894 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
895 LuaValue::Function(c) => c.clone(),
896 _ => unreachable!(),
897 }),
898 _ => None,
899 };
900 (cl, Some(ci_idx), func_val, what)
901 };
902
903 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
904 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
905
906 if what.contains(&b'f') {
907 state.push(func_val);
908 }
909 if what.contains(&b'L') {
910 let _ = collect_valid_lines(state, cl.as_ref());
912 }
913 status
914}
915
916#[inline]
922fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
923 if pc < jmptarget {
924 -1
925 } else {
926 pc
927 }
928}
929
930fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
934 let mut setreg: i32 = -1;
935 let mut jmptarget: i32 = 0;
936
937 let effective_lastpc = if p
940 .code
941 .get(lastpc as usize)
942 .map_or(false, |i| i.is_mm_mode())
943 {
944 lastpc - 1
945 } else {
946 lastpc
947 };
948
949 for pc in 0..effective_lastpc {
950 let instr = p.code[pc as usize];
951 let op = instr.opcode();
952 let a = instr.arg_a() as i32;
953
954 let change = match op {
955 OpCode::LoadNil => {
956 let b = instr.arg_b() as i32;
957 a <= reg && reg <= a + b
958 }
959 OpCode::TForCall => reg >= a + 2,
960 OpCode::Call | OpCode::TailCall => reg >= a,
961 OpCode::Jmp => {
962 let b = instr.arg_s_j();
963 let dest = pc + 1 + b;
964 if dest <= effective_lastpc && dest > jmptarget {
965 jmptarget = dest;
966 }
967 false
968 }
969 _ => {
970 instr.test_a_mode() && reg == a
973 }
974 };
975
976 if change {
977 setreg = filter_pc(pc, jmptarget);
978 }
979 }
980 setreg
981}
982
983fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
988 match p.k.get(index) {
991 Some(LuaValue::Str(s)) => {
992 *name = s.as_bytes();
994 Some(b"constant")
995 }
996 _ => {
997 *name = b"?";
998 None
999 }
1000 }
1001}
1002
1003fn basic_get_obj_name<'a>(
1007 p: &'a LuaProto,
1008 ppc: &mut i32,
1009 reg: i32,
1010 name: &mut &'a [u8],
1011) -> Option<&'static [u8]> {
1012 let pc = *ppc;
1013 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
1015 *name = local_name;
1016 return Some(b"local");
1017 }
1018
1019 *ppc = find_set_reg(p, pc, reg);
1020 let pc = *ppc;
1021
1022 if pc == -1 {
1023 return None;
1024 }
1025
1026 let instr = p.code[pc as usize];
1027 let op = instr.opcode();
1028 match op {
1029 OpCode::Move => {
1030 let b = instr.arg_b() as i32;
1031 if b < instr.arg_a() as i32 {
1032 return basic_get_obj_name(p, ppc, b, name);
1033 }
1034 }
1035 OpCode::GetUpVal => {
1036 *name = upval_name(p, instr.arg_b() as usize);
1037 return Some(b"upvalue");
1038 }
1039 OpCode::LoadK => {
1040 return kname(p, instr.arg_bx() as usize, name);
1041 }
1042 OpCode::LoadKx => {
1043 let next = p.code[(pc + 1) as usize];
1044 return kname(p, next.arg_ax() as usize, name);
1045 }
1046 _ => {}
1047 }
1048 None
1049}
1050
1051fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
1055 let mut pc = pc;
1056 let what = basic_get_obj_name(p, &mut pc, c, name);
1058 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1059 *name = b"?";
1060 }
1061}
1062
1063fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1066 let c = instr.arg_c() as i32;
1067 if instr.arg_k() != 0 {
1069 kname(p, c as usize, name);
1070 } else {
1071 rname(p, pc, c, name);
1072 }
1073}
1074
1075fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1079 let t = instr.arg_b() as usize;
1080 let mut name: &[u8] = b"?";
1081 if isup {
1082 name = upval_name(p, t);
1083 } else {
1084 let mut pc = pc;
1085 let what = basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1086 if !matches!(what, Some(kind) if kind == b"local" || kind == b"upvalue") {
1087 name = b"?";
1088 }
1089 }
1090 if name == LUA_ENV {
1091 b"global"
1092 } else {
1093 b"field"
1094 }
1095}
1096
1097fn get_obj_name<'a>(
1101 p: &'a LuaProto,
1102 lastpc: i32,
1103 reg: i32,
1104 name: &mut &'a [u8],
1105) -> Option<&'static [u8]> {
1106 let mut lastpc = lastpc;
1107 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1108 if kind.is_some() {
1109 return kind;
1110 }
1111
1112 if lastpc == -1 {
1113 return None;
1114 }
1115
1116 let instr = p.code[lastpc as usize];
1117 let op = instr.opcode();
1118 match op {
1119 OpCode::GetTabUp => {
1120 let k = instr.arg_c() as usize;
1121 kname(p, k, name);
1122 Some(is_env(p, lastpc, instr, true))
1123 }
1124 OpCode::GetTable => {
1125 let k = instr.arg_c() as i32;
1126 rname(p, lastpc, k, name);
1127 Some(is_env(p, lastpc, instr, false))
1128 }
1129 OpCode::GetI => {
1130 *name = b"integer index";
1131 Some(b"field")
1132 }
1133 OpCode::GetField => {
1134 let k = instr.arg_c() as usize;
1135 kname(p, k, name);
1136 Some(is_env(p, lastpc, instr, false))
1137 }
1138 OpCode::Self_ => {
1139 rkname(p, lastpc, instr, name);
1140 Some(b"method")
1141 }
1142 _ => None,
1143 }
1144}
1145
1146fn funcname_from_code<'a>(
1153 state: &LuaState,
1154 p: &'a LuaProto,
1155 pc: i32,
1156 name: &mut Option<Vec<u8>>,
1157) -> Option<&'static [u8]> {
1158 let instr = p.code[pc as usize];
1159 let op = instr.opcode();
1160
1161 match op {
1162 OpCode::Call | OpCode::TailCall => {
1163 let mut name_bytes: &[u8] = b"?";
1164 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1165 *name = Some(name_bytes.to_vec());
1166 kind
1167 }
1168 OpCode::TForCall => {
1169 *name = Some(b"for iterator".to_vec());
1170 Some(b"for iterator")
1171 }
1172 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1174 get_tm_name(state, TagMethod::Index, name)
1175 }
1176 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1177 get_tm_name(state, TagMethod::NewIndex, name)
1178 }
1179 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1180 let tm_idx = instr.arg_c() as u8;
1183 let tm = TagMethod::from_u8(tm_idx);
1184 get_tm_name(state, tm, name)
1185 }
1186 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1187 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1188 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1189 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1190 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1191 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1192 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1193 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1194 _ => None,
1195 }
1196}
1197
1198fn get_tm_name(
1204 state: &LuaState,
1205 tm: TagMethod,
1206 name: &mut Option<Vec<u8>>,
1207) -> Option<&'static [u8]> {
1208 let raw_bytes: Vec<u8> = state
1212 .global()
1213 .tm_name(tm)
1214 .map(|s| s.as_bytes().to_vec())
1215 .unwrap_or_default();
1216 let stripped = raw_bytes.strip_prefix(b"__").unwrap_or(&raw_bytes).to_vec();
1217 *name = Some(stripped);
1218 Some(b"metamethod")
1219}
1220
1221fn funcname_from_call<'a>(
1224 state: &'a LuaState,
1225 ci: &CallInfo,
1226 name: &mut Option<Vec<u8>>,
1227) -> Option<&'static [u8]> {
1228 if ci.callstatus & CIST_HOOKED != 0 {
1229 *name = Some(b"?".to_vec());
1230 return Some(b"hook");
1231 }
1232 if ci.callstatus & CIST_FIN != 0 {
1233 *name = Some(b"__gc".to_vec());
1234 return Some(b"metamethod");
1235 }
1236 if ci.is_lua() {
1237 let proto = ci_lua_proto(ci, state);
1238 return funcname_from_code(state, &proto, current_pc(ci), name);
1239 }
1240 None
1241}
1242
1243fn in_stack(ci: &CallInfo, val_idx: StackIdx) -> i32 {
1254 let base = StackIdx(ci.func.0 + 1);
1255 let ci_top = ci.top;
1258 let mut pos = 0i32;
1259 let mut cur = base;
1260 while cur.0 < ci_top.0 {
1261 if cur == val_idx {
1262 return pos;
1263 }
1264 cur = StackIdx(cur.0 + 1);
1265 pos += 1;
1266 }
1267 -1
1268}
1269
1270fn get_upval_name<'a>(
1279 ci: &CallInfo,
1280 val_idx: StackIdx,
1281 name: &mut &'a [u8],
1282 state: &'a LuaState,
1283) -> Option<&'static [u8]> {
1284 let proto = ci_lua_proto(ci, state);
1285 let lua_cl = match state.get_at(ci.func) {
1288 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1289 _ => return None,
1290 };
1291 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1292 let upval = upval_slot.get();
1293 let state = upval.slot().clone();
1294 if let lua_types::UpValState::Open { idx, .. } = state {
1295 if idx == val_idx {
1296 let _ = upval_name(&proto, i);
1299 *name = b"upvalue";
1300 return Some(b"upvalue");
1301 }
1302 }
1303 }
1304 None
1305}
1306
1307fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1312 match (kind, name) {
1313 (Some(k), Some(n)) => {
1314 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1315 out.extend_from_slice(b" (");
1316 out.extend_from_slice(k);
1317 out.extend_from_slice(b" '");
1318 out.extend_from_slice(n);
1319 out.extend_from_slice(b"')");
1320 out
1321 }
1322 _ => Vec::new(),
1323 }
1324}
1325
1326fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1330 let ci_idx = state.current_ci_idx();
1331 let ci = state.get_ci(ci_idx).clone();
1332 let mut kind: Option<&[u8]> = None;
1333 let mut name_owned: Vec<u8> = b"?".to_vec();
1334
1335 if ci.is_lua() {
1336 let mut up_name: &[u8] = b"?";
1337 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1338 if kind.is_some() {
1339 name_owned = up_name.to_vec();
1340 } else {
1341 let reg = in_stack(&ci, val_idx);
1342 if reg >= 0 {
1343 let proto = ci_lua_proto(&ci, state);
1344 let mut nref: &[u8] = b"?";
1345 let pc = current_pc(&ci);
1346 let k = get_obj_name(&proto, pc, reg, &mut nref);
1347 kind = k;
1348 if kind.is_some() {
1349 name_owned = nref.to_vec();
1350 }
1351 }
1352 }
1353 }
1354 format_var_info(
1355 kind,
1356 if kind.is_some() {
1357 Some(&name_owned)
1358 } else {
1359 None
1360 },
1361 )
1362}
1363
1364fn typeerror_inner(state: &LuaState, val: &LuaValue, op: &[u8], extra: &[u8]) -> LuaError {
1369 let t = state.obj_type_name(val);
1370 let mut msg = Vec::new();
1371 msg.extend_from_slice(b"attempt to ");
1372 msg.extend_from_slice(op);
1373 msg.extend_from_slice(b" a ");
1374 msg.extend_from_slice(&t);
1375 msg.extend_from_slice(b" value");
1376 msg.extend_from_slice(extra);
1377 prefixed_runtime(state, msg)
1378}
1379
1380pub(crate) fn type_error(
1384 state: &LuaState,
1385 val: &LuaValue,
1386 val_idx: StackIdx,
1387 op: &[u8],
1388) -> LuaError {
1389 let extra = var_info(state, val_idx);
1390 typeerror_inner(state, val, op, &extra)
1391}
1392
1393pub(crate) fn type_error_with_hint(
1399 state: &LuaState,
1400 val: &LuaValue,
1401 op: &[u8],
1402 kind: &[u8],
1403 name: &[u8],
1404) -> LuaError {
1405 let extra = format_var_info(Some(kind), Some(name));
1406 let t = obj_type_name_static(val);
1407 let mut msg = Vec::new();
1408 msg.extend_from_slice(b"attempt to ");
1409 msg.extend_from_slice(op);
1410 msg.extend_from_slice(b" a ");
1411 msg.extend_from_slice(t);
1412 msg.extend_from_slice(b" value");
1413 msg.extend_from_slice(&extra);
1414 prefixed_runtime(state, msg)
1415}
1416
1417fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1420 match val {
1421 LuaValue::Nil => b"nil",
1422 LuaValue::Bool(_) => b"boolean",
1423 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1424 LuaValue::Str(_) => b"string",
1425 LuaValue::Table(_) => b"table",
1426 LuaValue::Function(_) => b"function",
1427 LuaValue::UserData(_) => b"userdata",
1428 LuaValue::LightUserData(_) => b"light userdata",
1429 LuaValue::Thread(_) => b"thread",
1430 }
1431}
1432
1433pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1437 let ci_idx = state.current_ci_idx();
1438 let ci = state.get_ci(ci_idx).clone();
1439 let mut name: Option<Vec<u8>> = None;
1440 let kind = funcname_from_call(state, &ci, &mut name);
1441 let extra = if kind.is_some() {
1442 format_var_info(kind, name.as_deref())
1443 } else {
1444 var_info(state, val_idx)
1445 };
1446 typeerror_inner(state, val, b"call", &extra)
1447}
1448
1449pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1452 if matches!(
1456 state.global().lua_version,
1457 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1458 ) {
1459 let mut msg = Vec::new();
1460 msg.extend_from_slice(b"'for' ");
1461 msg.extend_from_slice(what);
1462 msg.extend_from_slice(b" must be a number");
1463 return prefixed_runtime(state, msg);
1464 }
1465 let t = crate::tagmethods::obj_type_name(state, val)
1466 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1467 let mut msg = Vec::new();
1468 msg.extend_from_slice(b"bad 'for' ");
1469 msg.extend_from_slice(what);
1470 msg.extend_from_slice(b" (number expected, got ");
1471 msg.extend_from_slice(&t);
1472 msg.push(b')');
1473 prefixed_runtime(state, msg)
1474}
1475
1476pub(crate) fn op_int_error(
1480 state: &LuaState,
1481 p1: &LuaValue,
1482 p1_idx: StackIdx,
1483 p2: &LuaValue,
1484 p2_idx: StackIdx,
1485 msg: &[u8],
1486) -> LuaError {
1487 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1489 (p1, p1_idx)
1490 } else {
1491 (p2, p2_idx)
1492 };
1493 type_error(state, bad_val, bad_idx, msg)
1494}
1495
1496pub(crate) fn to_int_error(
1502 state: &LuaState,
1503 p1: &LuaValue,
1504 p1_idx: Option<StackIdx>,
1505 _p2: &LuaValue,
1506 p2_idx: Option<StackIdx>,
1507) -> LuaError {
1508 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1509 p1_idx
1510 } else {
1511 p2_idx
1512 };
1513 let extra = match bad_idx {
1514 Some(idx) => var_info(state, idx),
1515 None => Vec::new(),
1516 };
1517 let mut msg = Vec::new();
1518 msg.extend_from_slice(b"number");
1519 msg.extend_from_slice(&extra);
1520 msg.extend_from_slice(b" has no integer representation");
1521 prefixed_runtime(state, msg)
1522}
1523
1524pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1527 let t1 = state.obj_type_name(p1);
1529 let t2 = state.obj_type_name(p2);
1530 let msg = if t1 == t2 {
1532 let mut m = Vec::new();
1533 m.extend_from_slice(b"attempt to compare two ");
1534 m.extend_from_slice(&t1);
1535 m.extend_from_slice(b" values");
1536 m
1537 } else {
1538 let mut m = Vec::new();
1539 m.extend_from_slice(b"attempt to compare ");
1540 m.extend_from_slice(&t1);
1541 m.extend_from_slice(b" with ");
1542 m.extend_from_slice(&t2);
1543 m
1544 };
1545 prefixed_runtime(state, msg)
1546}
1547
1548pub(crate) fn add_info(
1557 _state: Option<&mut LuaState>,
1558 msg: &[u8],
1559 src: Option<&LuaString>,
1560 line: i32,
1561 unknown_line_as_question: bool,
1562) -> Vec<u8> {
1563 let mut buff = [0u8; LUA_IDSIZE];
1565 if let Some(src) = src {
1566 chunk_id(&mut buff, src.as_bytes(), src.len());
1569 } else if unknown_line_as_question {
1570 let mut out = Vec::with_capacity(5 + msg.len());
1571 out.extend_from_slice(b"?:?: ");
1572 out.extend_from_slice(msg);
1573 return out;
1574 } else {
1575 buff[0] = b'?';
1576 }
1577 let src_part = buff
1580 .iter()
1581 .position(|&b| b == 0)
1582 .map_or(&buff[..], |n| &buff[..n]);
1583 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1584 out.extend_from_slice(src_part);
1585 out.push(b':');
1586 let line_str = line.to_string();
1588 out.extend_from_slice(line_str.as_bytes());
1589 out.extend_from_slice(b": ");
1590 out.extend_from_slice(msg);
1591 out
1592}
1593
1594fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1599 if p.lineinfo.is_empty() {
1600 return false;
1601 }
1602
1603 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1604 let mut delta: i32 = 0;
1605 let mut pc = oldpc;
1606 loop {
1607 pc += 1;
1608 if pc as usize >= p.lineinfo.len() {
1609 break;
1610 }
1611 let lineinfo = p.lineinfo[pc as usize];
1612 if lineinfo == ABS_LINE_INFO {
1613 break;
1614 }
1615 delta += lineinfo as i32;
1616 if pc == newpc {
1617 return delta != 0;
1618 }
1619 }
1620 }
1621 get_func_line(p, oldpc) != get_func_line(p, newpc)
1622}
1623
1624pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1630 let ci_idx = state.current_ci_idx();
1631 let ci = state.get_ci(ci_idx).clone();
1632 state.get_ci_mut(ci_idx).set_trap(true);
1633 let proto = ci_lua_proto(&ci, state);
1634
1635 if ci.saved_pc() == 0 {
1636 if proto.is_vararg {
1637 return Ok(0);
1638 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1639 state.hook_call(ci_idx)?;
1641 }
1642 }
1643 Ok(1)
1644}
1645
1646pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
1656 let ci_idx = state.current_ci_idx();
1657 let ci = state.get_ci(ci_idx).clone();
1658
1659 let mask = state.hook_mask();
1660
1661 if !state.allowhook {
1662 return Ok(1);
1663 }
1664
1665 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
1666 state.get_ci_mut(ci_idx).set_trap(false);
1667 return Ok(0);
1668 }
1669
1670 let next_pc = pc + 1;
1671 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
1672
1673 let counthook = if mask & LUA_MASKCOUNT != 0 {
1674 let hc = state.hook_count() - 1;
1675 state.set_hook_count(hc);
1676 hc == 0
1677 } else {
1678 false
1679 };
1680
1681 if counthook {
1682 state.reset_hook_count();
1683 } else if mask & LUA_MASKLINE == 0 {
1684 return Ok(1);
1685 }
1686
1687 if counthook {
1692 if let Some(err) = state.sandbox_charge_interval() {
1693 return Err(err);
1694 }
1695 }
1696
1697 if ci.callstatus & CIST_HOOKYIELD != 0 {
1698 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
1699 return Ok(1);
1700 }
1701
1702 if state.ci_lua_closure(ci_idx).is_none() {
1703 return Ok(1);
1704 }
1705
1706 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
1709 if !cur_instr.is_in_top() {
1710 let ci_top = state.get_ci(ci_idx).top;
1711 state.set_top(ci_top);
1712 }
1713
1714 if counthook {
1715 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
1717 }
1718
1719 if mask & LUA_MASKLINE != 0 {
1720 let proto = ci_lua_proto(&ci, state);
1721 let oldpc = if state.old_pc() < proto.code.len() as u32 {
1722 state.old_pc() as i32
1723 } else {
1724 0
1725 };
1726 let npci = next_pc as i32 - 1;
1728
1729 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
1730 let newline = get_func_line(&proto, npci);
1731 state.call_hook_event(LUA_HOOKLINE, newline)?;
1733 }
1734 state.set_old_pc(npci as u32);
1735 }
1736
1737 if state.status() == lua_types::status::LuaStatus::Yield {
1738 if counthook {
1739 state.set_hook_count(1);
1740 }
1741 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
1742 return Err(LuaError::Yield);
1744 }
1745
1746 Ok(1)
1747}
1748
1749fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
1757 out.fill(0);
1758 let n = crate::object::chunk_id(&mut out[..], source);
1759 if n < out.len() {
1760 out[n] = 0;
1761 }
1762}
1763
1764fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
1768 crate::func::get_local_name(p, n, pc)
1769}
1770
1771fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
1773 get_local_name(&cl.proto, n, pc)
1774}
1775
1776fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
1790 match state.get_at(ci.func) {
1791 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
1792 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
1793 }
1794}
1795
1796