1#[allow(unused_imports)]
7use crate::prelude::*;
8use crate::state::{
9 CallInfo, GcRef, LuaClosure, LuaClosureLua, LuaProto, LuaState, LuaTable, LuaValue, CIST_FIN,
10 CIST_HOOKED, CIST_HOOKYIELD, CIST_TAIL, CIST_TRAN,
11};
12use crate::vm::InstructionExt;
13use lua_types::error::LuaError;
14use lua_types::opcode::Instruction;
15use lua_types::{CallInfoIdx, LuaString, StackIdx};
16
17const ABS_LINE_INFO: i8 = -0x80_i8;
20
21const MAX_IWTH_ABS: i32 = 128;
22
23const LUA_IDSIZE: usize = 60;
25
26const LUA_MASKLINE: u8 = 1 << 2;
27const LUA_MASKCOUNT: u8 = 1 << 3;
28
29const LUA_HOOKLINE: i32 = 2;
30const LUA_HOOKCOUNT: i32 = 3;
31
32const LUA_ENV: &[u8] = b"_ENV";
33
34fn runtime_bytes(msg: Vec<u8>) -> LuaError {
39 LuaError::Runtime(lua_types::LuaValue::Str(lua_types::GcRef::new(
40 lua_types::LuaString::from_bytes(msg),
41 )))
42}
43
44pub(crate) fn prefixed_runtime_pub(state: &LuaState, msg: Vec<u8>) -> LuaError {
52 prefixed_runtime(state, msg)
53}
54
55fn prefixed_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
56 let ci_idx = state.current_ci_idx();
57 let ci = state.get_ci(ci_idx).clone();
58 if !ci.is_lua() {
59 return runtime_bytes(msg);
60 }
61 let proto = ci_lua_proto(&ci, state);
62 let src = proto.source_string();
63 let line = get_current_line(&ci, state);
64 let unknown_line_as_question =
65 src.is_none() && state.global().lua_version == lua_types::LuaVersion::V55;
66 let prefixed = add_info(
67 None,
68 &msg,
69 src.map(|s| &**s),
70 line,
71 unknown_line_as_question,
72 );
73 runtime_bytes(prefixed)
74}
75
76pub fn c_api_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
77 let ci_idx = state.current_ci_idx();
78 if let Some(parent_idx) = state.prev_ci(ci_idx) {
79 let parent_ci = state.get_ci(parent_idx).clone();
80 if parent_ci.is_lua() {
81 let proto = ci_lua_proto(&parent_ci, state);
82 let src = proto.source_string();
83 let line = get_current_line(&parent_ci, state);
84 let unknown_line_as_question =
85 src.is_none() && state.global().lua_version == lua_types::LuaVersion::V55;
86 let prefixed = add_info(
87 None,
88 &msg,
89 src.map(|s| &**s),
90 line,
91 unknown_line_as_question,
92 );
93 return runtime_bytes(prefixed);
94 }
95 }
96 runtime_bytes(msg)
97}
98
99#[allow(dead_code)]
109fn find_func_in_table(
110 table: &LuaTable,
111 target: &LuaValue,
112 prefix: &[u8],
113 depth: u8,
114) -> Option<Vec<u8>> {
115 let mut key = LuaValue::Nil;
116 loop {
117 let (k, v) = match table.next_pair(&key) {
118 Some(pair) => pair,
119 None => break,
120 };
121 if !matches!(v, LuaValue::Nil) {
122 let key_bytes: Option<Vec<u8>> = match &k {
123 LuaValue::Str(s) => Some(s.as_bytes().to_vec()),
124 _ => None,
125 };
126 if let Some(kb) = key_bytes {
127 if &v == target {
128 if prefix.is_empty() {
129 return Some(kb);
130 }
131 let mut result = prefix.to_vec();
132 result.push(b'.');
133 result.extend_from_slice(&kb);
134 return Some(result);
135 }
136 if depth > 0 {
137 if let LuaValue::Table(sub) = &v {
138 let new_prefix = if prefix.is_empty() {
139 kb.clone()
140 } else {
141 let mut p = prefix.to_vec();
142 p.push(b'.');
143 p.extend_from_slice(&kb);
144 p
145 };
146 if let Some(name) =
147 find_func_in_table(&**sub, target, &new_prefix, depth - 1)
148 {
149 return Some(name);
150 }
151 }
152 }
153 }
154 }
155 key = k;
156 }
157 None
158}
159
160#[allow(dead_code)]
168fn find_func_name_in_globals(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
169 let globals = state.global().globals.clone();
170 if let LuaValue::Table(globals_table) = globals {
171 find_func_in_table(&*globals_table, func_val, b"", 1)
172 } else {
173 None
174 }
175}
176
177fn find_func_name_in_loaded(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
184 let registry = state.global().l_registry.clone();
185 let loaded = match registry {
186 LuaValue::Table(ref reg_table) => reg_table.get_str_bytes(b"_LOADED"),
187 _ => return None,
188 };
189 let loaded_table = match loaded {
190 LuaValue::Table(t) => t,
191 _ => return None,
192 };
193 find_func_in_table(&*loaded_table, func_val, b"", 1)
194}
195
196fn arg_error_global_name(
222 state: &LuaState,
223 ar: &LuaDebug,
224 version: lua_types::LuaVersion,
225) -> Option<Vec<u8>> {
226 if version == lua_types::LuaVersion::V51 {
227 return None;
228 }
229 let keeps_global_prefix = version == lua_types::LuaVersion::V52;
230 let ci_idx = ar.i_ci?;
231 let func_slot = state.get_ci(ci_idx).func;
232 let func_val = state.get_at(func_slot).clone();
233 let found = find_func_name_in_loaded(state, &func_val)?;
234 if !keeps_global_prefix && found.starts_with(b"_G.") {
235 Some(found[3..].to_vec())
236 } else {
237 Some(found)
238 }
239}
240
241pub fn arg_error_impl(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> LuaError {
245 let mut ar = LuaDebug::default();
246 if !get_stack(state, 0, &mut ar) {
247 let msg = format!(
248 "bad argument #{} ({})",
249 arg,
250 String::from_utf8_lossy(extramsg)
251 );
252 return c_api_runtime(state, msg.into_bytes());
253 }
254 get_info(state, b"n", &mut ar);
255 if ar.namewhat.as_deref() == Some(b"method") {
256 arg -= 1;
257 if arg == 0 {
258 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
259 let msg = format!(
260 "calling '{}' on bad self ({})",
261 String::from_utf8_lossy(&name),
262 String::from_utf8_lossy(extramsg)
263 );
264 return c_api_runtime(state, msg.into_bytes());
265 }
266 }
267 let version = state.global().lua_version;
268 let fname = ar
269 .name
270 .clone()
271 .or_else(|| arg_error_global_name(state, &ar, version))
272 .unwrap_or_else(|| b"?".to_vec());
273 let msg = format!(
274 "bad argument #{} to '{}' ({})",
275 arg,
276 String::from_utf8_lossy(&fname),
277 String::from_utf8_lossy(extramsg)
278 );
279 c_api_runtime(state, msg.into_bytes())
280}
281
282pub struct LuaDebug {
291 pub event: i32,
292 pub name: Option<Vec<u8>>,
293 pub namewhat: Option<&'static [u8]>,
294 pub what: Option<&'static [u8]>,
295 pub source: Option<Vec<u8>>,
296 pub srclen: usize,
297 pub currentline: i32,
298 pub linedefined: i32,
299 pub lastlinedefined: i32,
300 pub nups: u8,
301 pub nparams: u8,
302 pub isvararg: bool,
303 pub istailcall: bool,
304 pub extraargs: u8,
305 pub ftransfer: u16,
306 pub ntransfer: u16,
307 pub short_src: [u8; LUA_IDSIZE],
308 pub i_ci: Option<CallInfoIdx>,
311}
312
313impl Default for LuaDebug {
314 fn default() -> Self {
315 LuaDebug {
316 event: 0,
317 name: None,
318 namewhat: None,
319 what: None,
320 source: None,
321 srclen: 0,
322 currentline: -1,
323 linedefined: -1,
324 lastlinedefined: -1,
325 nups: 0,
326 nparams: 0,
327 isvararg: false,
328 istailcall: false,
329 extraargs: 0,
330 ftransfer: 0,
331 ntransfer: 0,
332 short_src: [0u8; LUA_IDSIZE],
333 i_ci: None,
334 }
335 }
336}
337
338#[inline]
341fn is_lua_closure(cl: Option<&LuaClosure>) -> bool {
342 matches!(cl, Some(LuaClosure::Lua(_)))
343}
344
345fn current_pc(ci: &CallInfo) -> i32 {
355 debug_assert!(ci.is_lua());
356 ci.saved_pc().saturating_sub(1) as i32
357}
358
359fn get_baseline(f: &LuaProto, pc: i32, basepc: &mut i32) -> i32 {
367 if f.abslineinfo.is_empty() || pc < f.abslineinfo[0].pc {
368 *basepc = -1;
369 return f.linedefined;
370 }
371 let mut i = (pc as u32 / MAX_IWTH_ABS as u32).saturating_sub(1) as usize;
372 debug_assert!(
373 i < f.abslineinfo.len() && f.abslineinfo[i].pc <= pc,
374 "getbaseline: estimate is not a lower bound"
375 );
376 while i + 1 < f.abslineinfo.len() && pc >= f.abslineinfo[i + 1].pc {
377 i += 1;
378 }
379 *basepc = f.abslineinfo[i].pc;
380 f.abslineinfo[i].line
381}
382
383pub(crate) fn get_func_line(f: &LuaProto, pc: i32) -> i32 {
387 if f.lineinfo.is_empty() {
388 return -1;
389 }
390 let mut basepc: i32 = 0;
391 let mut baseline = get_baseline(f, pc, &mut basepc);
392 while basepc < pc {
396 basepc += 1;
397 debug_assert!(
398 f.lineinfo[basepc as usize] != ABS_LINE_INFO,
399 "get_func_line: hit ABSLINEINFO in incremental walk"
400 );
401 baseline += f.lineinfo[basepc as usize] as i32;
402 }
403 baseline
404}
405
406fn get_current_line(ci: &CallInfo, state: &LuaState) -> i32 {
409 let proto = ci_lua_proto(ci, state);
410 get_func_line(&proto, current_pc(ci))
411}
412
413pub(crate) fn arm_traps(state: &mut LuaState) {
425 set_traps(state);
426}
427
428fn set_traps(state: &mut LuaState) {
429 for ci in state.call_stack_mut().iter_mut() {
430 if ci.is_lua() {
431 ci.set_trap(true);
432 }
433 }
434}
435
436pub fn set_hook(
439 state: &mut LuaState,
440 func: Option<Box<dyn FnMut(&mut LuaState, &LuaDebug)>>,
441 mask: i32,
442 count: i32,
443) {
444 let (func, mask) = if func.is_none() || mask == 0 {
445 (None, 0i32)
446 } else {
447 (func, mask)
448 };
449 state.set_hook(func);
450 state.set_base_hook_count(count);
451 state.reset_hook_count();
452 state.set_hook_mask(mask as u8);
453 if mask != 0 {
454 set_traps(state);
455 }
456}
457
458pub 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 if state.global().lua_version == lua_types::LuaVersion::V51 {
490 return get_stack_51(state, level, ar);
491 }
492 let mut remaining = level;
493 let mut ci_idx = state.current_ci_idx();
494 loop {
495 if remaining == 0 {
496 break;
497 }
498 match state.prev_ci(ci_idx) {
499 Some(prev) => {
500 ci_idx = prev;
501 remaining -= 1;
502 }
503 None => {
504 return false;
505 }
506 }
507 }
508 if !state.is_base_ci(ci_idx) {
509 ar.i_ci = Some(ci_idx);
510 true
511 } else {
512 false
513 }
514}
515
516fn get_stack_51(state: &LuaState, level: i32, ar: &mut LuaDebug) -> bool {
527 let mut remaining = level;
528 let mut ci_idx = state.current_ci_idx();
529 loop {
530 if remaining <= 0 || state.is_base_ci(ci_idx) {
531 break;
532 }
533 remaining -= 1;
534 let ci = state.get_ci(ci_idx);
535 if ci.is_lua() {
536 remaining -= ci.tailcalls as i32;
537 }
538 match state.prev_ci(ci_idx) {
539 Some(prev) => ci_idx = prev,
540 None => break,
541 }
542 }
543 if remaining == 0 && !state.is_base_ci(ci_idx) {
544 ar.i_ci = Some(ci_idx);
545 true
546 } else if remaining < 0 {
547 ar.i_ci = Some(CallInfoIdx(0));
548 true
549 } else {
550 false
551 }
552}
553
554fn visible_upvalue_count_51(p: &LuaProto) -> usize {
565 p.upvalues
566 .iter()
567 .filter(|uv| uv.name.as_ref().map_or(true, |s| s.as_bytes() != LUA_ENV))
568 .count()
569}
570
571fn upval_name(p: &LuaProto, uv: usize) -> &[u8] {
574 debug_assert!(uv < p.upvalues.len(), "upval_name: index out of range");
575 p.upvalues[uv]
576 .name
577 .as_ref()
578 .map_or(b"?" as &[u8], |s| s.as_bytes())
579}
580
581fn temporary_local_name(state: &LuaState, ci_is_lua: bool) -> &'static [u8] {
590 match state.global().lua_version {
591 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 => {
592 b"(*temporary)"
593 }
594 _ => {
595 if ci_is_lua {
596 b"(temporary)"
597 } else {
598 b"(C temporary)"
599 }
600 }
601 }
602}
603
604fn find_vararg(state: &LuaState, ci: &CallInfo, n: i32) -> Option<(StackIdx, &'static [u8])> {
615 let proto = ci_lua_proto(ci, state);
616 if proto.is_vararg {
617 let nextra = ci.nextra_args();
618 if n >= -(nextra as i32) {
619 let pos = ci.func - (nextra + n + 1);
621 let name: &'static [u8] = match state.global().lua_version {
622 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 => b"(*vararg)",
623 _ => b"(vararg)",
624 };
625 return Some((pos, name));
626 }
627 }
628 None
629}
630
631pub(crate) fn find_local(
643 state: &LuaState,
644 ci_idx: CallInfoIdx,
645 n: i32,
646 pos: Option<&mut StackIdx>,
647) -> Option<Vec<u8>> {
648 let ci = state.get_ci(ci_idx);
649 let base = ci.func + 1;
650 let mut name: Option<Vec<u8>> = None;
651
652 if ci.is_lua() {
653 if n < 0 {
654 if let Some((vpos, vname)) = find_vararg(state, ci, n) {
655 if let Some(out_pos) = pos {
656 *out_pos = vpos;
657 }
658 return Some(vname.to_vec());
659 }
660 return None;
661 } else {
662 let proto = ci_lua_proto(ci, state);
663 let pc = current_pc(ci);
664 name = crate::func::get_local_name(&proto, n, pc).map(|s| s.to_vec());
665 }
666 }
667
668 if name.is_none() {
669 let limit: u32 = if ci_idx == state.current_ci_idx() {
670 state.top_idx().0
671 } else {
672 ci.next
673 .map(|next| state.get_ci(next).func.0)
674 .unwrap_or_else(|| state.top_idx().0)
675 };
676 if n > 0 && limit.saturating_sub(base.0) >= n as u32 {
677 name = Some(temporary_local_name(state, ci.is_lua()).to_vec());
678 } else {
679 return None;
680 }
681 }
682
683 if let Some(out_pos) = pos {
684 *out_pos = base + (n - 1);
685 }
686 name
687}
688
689pub fn get_local(state: &mut LuaState, ar: Option<&LuaDebug>, n: i32) -> Option<Vec<u8>> {
694 if ar.is_none() {
695 let top_val = state.peek_top();
696 if !matches!(top_val, LuaValue::Function(LuaClosure::Lua(_))) {
697 return None;
698 }
699 let name_owned: Option<Vec<u8>> = {
702 let cl = match top_val {
703 LuaValue::Function(LuaClosure::Lua(ref cl)) => cl.clone(),
704 _ => unreachable!(),
705 };
706 get_local_name_from_closure(&cl, n, 0).map(|s| s.to_vec())
707 };
708 return name_owned;
709 }
710
711 let ar = ar.unwrap();
712 let ci_idx = ar.i_ci?;
713 let mut pos = StackIdx(0);
714 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
717
718 if name_owned.is_some() {
719 let val = state.get_at(pos).clone();
720 state.push(val);
721 }
722 name_owned
723}
724
725pub fn set_local(state: &mut LuaState, ar: &LuaDebug, n: i32) -> Option<Vec<u8>> {
729 let ci_idx = ar.i_ci?;
730 let mut pos = StackIdx(0);
731 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
732 if name_owned.is_some() {
733 let val = state.get_at(state.top_idx() - 1).clone();
734 state.set_at(pos, val);
735 state.pop_n(1);
736 }
737 name_owned
738}
739
740fn func_info(ar: &mut LuaDebug, cl: Option<&LuaClosure>) {
745 if !is_lua_closure(cl) {
746 ar.source = Some(b"=[C]".to_vec());
747 ar.srclen = b"=[C]".len();
748 ar.linedefined = -1;
749 ar.lastlinedefined = -1;
750 ar.what = Some(b"C");
751 } else {
752 let lua_cl = match cl {
753 Some(LuaClosure::Lua(cl)) => cl,
754 _ => unreachable!(),
755 };
756 let proto: &LuaProto = &lua_cl.proto;
757 if let Some(src) = proto.source_string() {
759 ar.source = Some(src.as_bytes().to_vec());
760 ar.srclen = src.as_bytes().len();
761 } else {
762 ar.source = Some(b"=?".to_vec());
763 ar.srclen = b"=?".len();
764 }
765 ar.linedefined = proto.linedefined;
766 ar.lastlinedefined = proto.lastlinedefined;
767 ar.what = Some(if ar.linedefined == 0 { b"main" } else { b"Lua" });
768 }
769 chunk_id(
770 &mut ar.short_src,
771 ar.source.as_deref().unwrap_or(b"?"),
772 ar.srclen,
773 );
774}
775
776fn next_line(p: &LuaProto, currentline: i32, pc: usize) -> i32 {
780 if p.lineinfo.get(pc).copied() != Some(ABS_LINE_INFO) {
782 currentline + p.lineinfo[pc] as i32
783 } else {
784 get_func_line(p, pc as i32)
785 }
786}
787
788fn collect_valid_lines(state: &mut LuaState, cl: Option<&LuaClosure>) -> Result<(), LuaError> {
792 if !is_lua_closure(cl) {
793 state.push(LuaValue::Nil);
794 return Ok(());
795 }
796 let lua_cl = match cl {
797 Some(LuaClosure::Lua(cl)) => cl.clone(),
798 _ => unreachable!(),
799 };
800 let proto: GcRef<LuaProto> = lua_cl.proto.clone();
801 let p: &LuaProto = &proto;
802
803 let mut currentline = p.linedefined;
804
805 let t = state.new_table();
806 state.push(LuaValue::Table(t.clone()));
807
808 if !p.lineinfo.is_empty() {
809 let v = LuaValue::Bool(true);
810
811 let start_i = if !p.is_vararg {
812 0usize
813 } else {
814 debug_assert!(
815 p.code.first().map(|i| i.is_vararg_prep()).unwrap_or(false),
816 "collect_valid_lines: first instruction of vararg should be OP_VARARGPREP"
817 );
818 currentline = next_line(p, currentline, 0);
819 1usize
820 };
821
822 for i in start_i..p.lineinfo.len() {
824 currentline = next_line(p, currentline, i);
825 t.raw_set_int(state, currentline as i64, v.clone())?;
826 }
827 }
828 Ok(())
829}
830
831fn get_func_name<'a>(
847 state: &'a LuaState,
848 ci: Option<&CallInfo>,
849 name: &mut Option<Vec<u8>>,
850) -> Option<&'static [u8]> {
851 let ci = ci?;
852 match state.global().lua_version {
853 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 => {
854 if ci.callstatus & CIST_TAIL != 0 {
855 return None;
856 }
857 funcname_from_caller_code(state, ci, false, name)
858 }
859 lua_types::LuaVersion::V53 => {
860 if ci.callstatus & CIST_FIN != 0 {
861 *name = Some(b"__gc".to_vec());
862 return Some(b"metamethod");
863 }
864 if ci.callstatus & CIST_TAIL != 0 {
865 return None;
866 }
867 funcname_from_caller_code(state, ci, true, name)
868 }
869 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 | _ => {
870 if ci.callstatus & CIST_TAIL != 0 {
871 return None;
872 }
873 let prev_ci = state.get_ci(ci.previous?).clone();
874 funcname_from_call(state, &prev_ci, name)
875 }
876 }
877}
878
879fn funcname_from_caller_code<'a>(
887 state: &'a LuaState,
888 ci: &CallInfo,
889 check_hooked: bool,
890 name: &mut Option<Vec<u8>>,
891) -> Option<&'static [u8]> {
892 let prev_ci = state.get_ci(ci.previous?).clone();
893 if !prev_ci.is_lua() {
894 return None;
895 }
896 if check_hooked && prev_ci.callstatus & CIST_HOOKED != 0 {
897 *name = Some(b"?".to_vec());
898 return Some(b"hook");
899 }
900 let proto = ci_lua_proto(&prev_ci, state);
901 funcname_from_code(state, &proto, current_pc(&prev_ci), name)
902}
903
904fn aux_get_info(
907 state: &LuaState,
908 what: &[u8],
909 ar: &mut LuaDebug,
910 cl: Option<&LuaClosure>,
911 ci: Option<&CallInfo>,
912) -> bool {
913 let mut status = true;
914 for &ch in what {
915 match ch {
916 b'S' => {
917 func_info(ar, cl);
918 }
919 b'l' => {
920 ar.currentline = match ci {
921 Some(ci) if ci.is_lua() => get_current_line(ci, state),
922 _ => -1,
923 };
924 }
925 b'u' => {
926 ar.nups = cl.map_or(0, |c| c.nupvalues() as u8);
927 match cl {
928 Some(LuaClosure::Lua(lua_cl)) => {
929 ar.isvararg = lua_cl.proto.is_vararg;
930 ar.nparams = lua_cl.proto.numparams;
931 if state.global().lua_version == lua_types::LuaVersion::V51 {
932 ar.nups = visible_upvalue_count_51(&lua_cl.proto) as u8;
933 }
934 }
935 _ => {
936 ar.isvararg = true;
937 ar.nparams = 0;
938 }
939 }
940 }
941 b't' => {
942 if let Some(ci) = ci {
943 ar.istailcall = ci.callstatus & CIST_TAIL != 0;
944 ar.extraargs = ci.call_metamethods;
945 } else {
946 ar.istailcall = false;
947 ar.extraargs = 0;
948 }
949 }
950 b'n' => {
951 let mut name: Option<Vec<u8>> = None;
952 ar.namewhat = get_func_name(state, ci, &mut name);
953 if ar.namewhat.is_none() {
954 ar.namewhat = Some(b"");
955 ar.name = None;
956 } else {
957 ar.name = name;
958 }
959 }
960 b'r' => match ci {
961 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
962 ar.ftransfer = ci.transfer_ftransfer();
963 ar.ntransfer = ci.transfer_ntransfer();
964 }
965 _ => {
966 ar.ftransfer = 0;
967 ar.ntransfer = 0;
968 }
969 },
970 b'L' | b'f' => {}
971 _ => {
972 status = false;
973 }
974 }
975 }
976 status
977}
978
979pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
982 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
983 let func_val = state.peek_at(state.top_idx() - 1).clone();
984 state.pop_n(1);
985 debug_assert!(
986 matches!(func_val, LuaValue::Function(_)),
987 "get_info: function expected"
988 );
989 let cl = match &func_val {
990 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
991 LuaValue::Function(c) => c.clone(),
992 _ => unreachable!(),
993 }),
994 _ => None,
995 };
996 (cl, None, func_val, &what[1..])
997 } else {
998 let ci_idx = match ar.i_ci {
999 Some(i) => i,
1000 None => return false,
1001 };
1002 if state.global().lua_version == lua_types::LuaVersion::V51
1003 && state.is_base_ci(ci_idx)
1004 {
1005 return get_info_tailcall_51(state, what, ar);
1006 }
1007 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
1008 debug_assert!(
1009 matches!(func_val, LuaValue::Function(_)),
1010 "get_info: non-function at ci->func"
1011 );
1012 let cl = match &func_val {
1013 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
1014 LuaValue::Function(c) => c.clone(),
1015 _ => unreachable!(),
1016 }),
1017 _ => None,
1018 };
1019 (cl, Some(ci_idx), func_val, what)
1020 };
1021
1022 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
1023 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
1024
1025 if what.contains(&b'f') {
1026 state.push(func_val);
1027 }
1028 if what.contains(&b'L') {
1029 let _ = collect_valid_lines(state, cl.as_ref());
1030 }
1031 status
1032}
1033
1034fn get_info_tailcall_51(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
1041 let what = if what.first() == Some(&b'>') {
1042 &what[1..]
1043 } else {
1044 what
1045 };
1046 info_tailcall(ar);
1047 let mut status = true;
1048 for &ch in what {
1049 if !matches!(ch, b'S' | b'l' | b'u' | b'n' | b't' | b'r' | b'L' | b'f') {
1050 status = false;
1051 }
1052 }
1053 if what.contains(&b'f') {
1054 state.push(LuaValue::Nil);
1055 }
1056 if what.contains(&b'L') {
1057 state.push(LuaValue::Nil);
1058 }
1059 status
1060}
1061
1062fn info_tailcall(ar: &mut LuaDebug) {
1064 ar.name = Some(Vec::new());
1065 ar.namewhat = Some(b"");
1066 ar.what = Some(b"tail");
1067 ar.linedefined = -1;
1068 ar.lastlinedefined = -1;
1069 ar.currentline = -1;
1070 ar.source = Some(b"=(tail call)".to_vec());
1071 ar.srclen = b"=(tail call)".len();
1072 chunk_id(&mut ar.short_src, b"=(tail call)", b"=(tail call)".len());
1073 ar.nups = 0;
1074 ar.istailcall = false;
1075}
1076
1077#[inline]
1083fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
1084 if pc < jmptarget {
1085 -1
1086 } else {
1087 pc
1088 }
1089}
1090
1091fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
1095 let mut setreg: i32 = -1;
1096 let mut jmptarget: i32 = 0;
1097
1098 let effective_lastpc = if p
1099 .code
1100 .get(lastpc as usize)
1101 .map_or(false, |i| i.is_mm_mode())
1102 {
1103 lastpc - 1
1104 } else {
1105 lastpc
1106 };
1107
1108 for pc in 0..effective_lastpc {
1109 let instr = p.code[pc as usize];
1110 let op = instr.opcode();
1111 let a = instr.arg_a() as i32;
1112
1113 let change = match op {
1114 OpCode::LoadNil => {
1115 let b = instr.arg_b() as i32;
1116 a <= reg && reg <= a + b
1117 }
1118 OpCode::TForCall => reg >= a + 2,
1119 OpCode::Call | OpCode::TailCall => reg >= a,
1120 OpCode::Jmp => {
1121 let b = instr.arg_s_j();
1122 let dest = pc + 1 + b;
1123 if dest <= effective_lastpc && dest > jmptarget {
1124 jmptarget = dest;
1125 }
1126 false
1127 }
1128 _ => {
1129 instr.test_a_mode() && reg == a
1130 }
1131 };
1132
1133 if change {
1134 setreg = filter_pc(pc, jmptarget);
1135 }
1136 }
1137 setreg
1138}
1139
1140fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
1145 match p.k.get(index) {
1146 Some(LuaValue::Str(s)) => {
1147 *name = s.as_bytes();
1148 Some(b"constant")
1149 }
1150 _ => {
1151 *name = b"?";
1152 None
1153 }
1154 }
1155}
1156
1157fn basic_get_obj_name<'a>(
1161 p: &'a LuaProto,
1162 ppc: &mut i32,
1163 reg: i32,
1164 name: &mut &'a [u8],
1165) -> Option<&'static [u8]> {
1166 let pc = *ppc;
1167 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
1169 *name = local_name;
1170 return Some(b"local");
1171 }
1172
1173 *ppc = find_set_reg(p, pc, reg);
1174 let pc = *ppc;
1175
1176 if pc == -1 {
1177 return None;
1178 }
1179
1180 let instr = p.code[pc as usize];
1181 let op = instr.opcode();
1182 match op {
1183 OpCode::Move => {
1184 let b = instr.arg_b() as i32;
1185 if b < instr.arg_a() as i32 {
1186 return basic_get_obj_name(p, ppc, b, name);
1187 }
1188 }
1189 OpCode::GetUpVal => {
1190 *name = upval_name(p, instr.arg_b() as usize);
1191 return Some(b"upvalue");
1192 }
1193 OpCode::LoadK => {
1194 return kname(p, instr.arg_bx() as usize, name);
1195 }
1196 OpCode::LoadKx => {
1197 let next = p.code[(pc + 1) as usize];
1198 return kname(p, next.arg_ax() as usize, name);
1199 }
1200 _ => {}
1201 }
1202 None
1203}
1204
1205fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
1209 let mut pc = pc;
1210 let what = basic_get_obj_name(p, &mut pc, c, name);
1211 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1212 *name = b"?";
1213 }
1214}
1215
1216fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1219 let c = instr.arg_c() as i32;
1220 if instr.arg_k() != 0 {
1221 kname(p, c as usize, name);
1222 } else {
1223 rname(p, pc, c, name);
1224 }
1225}
1226
1227fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1231 let t = instr.arg_b() as usize;
1232 let mut name: &[u8] = b"?";
1233 if isup {
1234 name = upval_name(p, t);
1235 } else {
1236 let mut pc = pc;
1237 let what = basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1238 if !matches!(what, Some(kind) if kind == b"local" || kind == b"upvalue") {
1239 name = b"?";
1240 }
1241 }
1242 if name == LUA_ENV {
1243 b"global"
1244 } else {
1245 b"field"
1246 }
1247}
1248
1249fn get_obj_name<'a>(
1253 p: &'a LuaProto,
1254 lastpc: i32,
1255 reg: i32,
1256 name: &mut &'a [u8],
1257) -> Option<&'static [u8]> {
1258 let mut lastpc = lastpc;
1259 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1260 if kind.is_some() {
1261 return kind;
1262 }
1263
1264 if lastpc == -1 {
1265 return None;
1266 }
1267
1268 let instr = p.code[lastpc as usize];
1269 let op = instr.opcode();
1270 match op {
1271 OpCode::GetTabUp => {
1272 let k = instr.arg_c() as usize;
1273 kname(p, k, name);
1274 Some(is_env(p, lastpc, instr, true))
1275 }
1276 OpCode::GetTable => {
1277 let k = instr.arg_c() as i32;
1278 rname(p, lastpc, k, name);
1279 Some(is_env(p, lastpc, instr, false))
1280 }
1281 OpCode::GetI => {
1282 *name = b"integer index";
1283 Some(b"field")
1284 }
1285 OpCode::GetField => {
1286 let k = instr.arg_c() as usize;
1287 kname(p, k, name);
1288 Some(is_env(p, lastpc, instr, false))
1289 }
1290 OpCode::Self_ => {
1291 rkname(p, lastpc, instr, name);
1292 Some(b"method")
1293 }
1294 _ => None,
1295 }
1296}
1297
1298fn funcname_from_code<'a>(
1305 state: &LuaState,
1306 p: &'a LuaProto,
1307 pc: i32,
1308 name: &mut Option<Vec<u8>>,
1309) -> Option<&'static [u8]> {
1310 let instr = p.code[pc as usize];
1311 let op = instr.opcode();
1312
1313 match op {
1314 OpCode::Call | OpCode::TailCall => {
1315 let mut name_bytes: &[u8] = b"?";
1316 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1317 *name = Some(name_bytes.to_vec());
1318 kind
1319 }
1320 OpCode::TForCall => {
1321 *name = Some(b"for iterator".to_vec());
1322 Some(b"for iterator")
1323 }
1324 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1326 get_tm_name(state, TagMethod::Index, name)
1327 }
1328 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1329 get_tm_name(state, TagMethod::NewIndex, name)
1330 }
1331 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1332 let tm_idx = instr.arg_c() as u8;
1333 let tm = TagMethod::from_u8(tm_idx);
1334 get_tm_name(state, tm, name)
1335 }
1336 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1337 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1338 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1339 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1340 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1341 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1342 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1343 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1344 _ => None,
1345 }
1346}
1347
1348fn get_tm_name(
1358 state: &LuaState,
1359 tm: TagMethod,
1360 name: &mut Option<Vec<u8>>,
1361) -> Option<&'static [u8]> {
1362 if state.global().lua_version == lua_types::LuaVersion::V51 {
1363 return None;
1364 }
1365 let raw_bytes: Vec<u8> = state
1368 .global()
1369 .tm_name(tm)
1370 .map(|s| s.as_bytes().to_vec())
1371 .unwrap_or_default();
1372 let keeps_prefix = matches!(
1373 state.global().lua_version,
1374 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1375 );
1376 let resolved = if keeps_prefix {
1377 raw_bytes
1378 } else {
1379 raw_bytes.strip_prefix(b"__").unwrap_or(&raw_bytes).to_vec()
1380 };
1381 *name = Some(resolved);
1382 Some(b"metamethod")
1383}
1384
1385fn funcname_from_call<'a>(
1388 state: &'a LuaState,
1389 ci: &CallInfo,
1390 name: &mut Option<Vec<u8>>,
1391) -> Option<&'static [u8]> {
1392 if ci.callstatus & CIST_HOOKED != 0 {
1393 *name = Some(b"?".to_vec());
1394 return Some(b"hook");
1395 }
1396 if ci.callstatus & CIST_FIN != 0 {
1397 *name = Some(b"__gc".to_vec());
1398 return Some(b"metamethod");
1399 }
1400 if ci.is_lua() {
1401 let proto = ci_lua_proto(ci, state);
1402 return funcname_from_code(state, &proto, current_pc(ci), name);
1403 }
1404 None
1405}
1406
1407fn in_stack(ci: &CallInfo, val_idx: StackIdx) -> i32 {
1416 let base = StackIdx(ci.func.0 + 1);
1417 let ci_top = ci.top;
1418 let mut pos = 0i32;
1419 let mut cur = base;
1420 while cur.0 < ci_top.0 {
1421 if cur == val_idx {
1422 return pos;
1423 }
1424 cur = StackIdx(cur.0 + 1);
1425 pos += 1;
1426 }
1427 -1
1428}
1429
1430fn get_upval_name<'a>(
1438 ci: &CallInfo,
1439 val_idx: StackIdx,
1440 name: &mut &'a [u8],
1441 state: &'a LuaState,
1442) -> Option<&'static [u8]> {
1443 let proto = ci_lua_proto(ci, state);
1444 let lua_cl = match state.get_at(ci.func) {
1445 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1446 _ => return None,
1447 };
1448 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1449 let upval = upval_slot.get();
1450 if let Some((_thread_id, idx)) = upval.try_open_payload() {
1451 if idx == val_idx {
1452 let _ = upval_name(&proto, i);
1453 *name = b"upvalue";
1454 return Some(b"upvalue");
1455 }
1456 }
1457 }
1458 None
1459}
1460
1461fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1466 match (kind, name) {
1467 (Some(k), Some(n)) => {
1468 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1469 out.extend_from_slice(b" (");
1470 out.extend_from_slice(k);
1471 out.extend_from_slice(b" '");
1472 out.extend_from_slice(n);
1473 out.extend_from_slice(b"')");
1474 out
1475 }
1476 _ => Vec::new(),
1477 }
1478}
1479
1480fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1484 let (kind, name) = var_info_parts(state, val_idx);
1485 format_var_info(kind.as_deref(), name.as_deref())
1486}
1487
1488fn var_info_parts(state: &LuaState, val_idx: StackIdx) -> (Option<Vec<u8>>, Option<Vec<u8>>) {
1495 let ci_idx = state.current_ci_idx();
1496 let ci = state.get_ci(ci_idx).clone();
1497 let mut kind: Option<&[u8]> = None;
1498 let mut name_owned: Vec<u8> = b"?".to_vec();
1499
1500 if ci.is_lua() {
1501 let mut up_name: &[u8] = b"?";
1502 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1503 if kind.is_some() {
1504 name_owned = up_name.to_vec();
1505 } else {
1506 let reg = in_stack(&ci, val_idx);
1507 if reg >= 0 {
1508 let proto = ci_lua_proto(&ci, state);
1509 let mut nref: &[u8] = b"?";
1510 let pc = current_pc(&ci);
1511 let k = get_obj_name(&proto, pc, reg, &mut nref);
1512 kind = k;
1513 if kind.is_some() {
1514 name_owned = nref.to_vec();
1515 }
1516 }
1517 }
1518 }
1519 match kind {
1520 Some(k) => (Some(k.to_vec()), Some(name_owned)),
1521 None => (None, None),
1522 }
1523}
1524
1525fn typeerror_inner_parts(
1536 state: &LuaState,
1537 val: &LuaValue,
1538 op: &[u8],
1539 kind: Option<&[u8]>,
1540 name: Option<&[u8]>,
1541) -> LuaError {
1542 let t = state.obj_type_name(val);
1543 let legacy_order = matches!(
1544 state.global().lua_version,
1545 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
1546 );
1547 let mut msg = Vec::new();
1548 msg.extend_from_slice(b"attempt to ");
1549 msg.extend_from_slice(op);
1550 if let (true, Some(k), Some(n)) = (legacy_order, kind, name) {
1551 msg.extend_from_slice(b" ");
1552 msg.extend_from_slice(k);
1553 msg.extend_from_slice(b" '");
1554 msg.extend_from_slice(n);
1555 msg.extend_from_slice(b"' (a ");
1556 msg.extend_from_slice(&t);
1557 msg.extend_from_slice(b" value)");
1558 } else {
1559 msg.extend_from_slice(b" a ");
1560 msg.extend_from_slice(&t);
1561 msg.extend_from_slice(b" value");
1562 msg.extend_from_slice(&format_var_info(kind, name));
1563 }
1564 prefixed_runtime(state, msg)
1565}
1566
1567pub(crate) fn type_error(
1571 state: &LuaState,
1572 val: &LuaValue,
1573 val_idx: StackIdx,
1574 op: &[u8],
1575) -> LuaError {
1576 let (kind, name) = var_info_parts(state, val_idx);
1577 typeerror_inner_parts(state, val, op, kind.as_deref(), name.as_deref())
1578}
1579
1580pub(crate) fn arith_type_error(
1595 state: &LuaState,
1596 val: &LuaValue,
1597 val_idx: StackIdx,
1598 op: &[u8],
1599 binary: bool,
1600) -> LuaError {
1601 let (kind, name) = var_info_parts(state, val_idx);
1602 let is_constant = matches!(kind.as_deref(), Some(b"constant"));
1603 let suppress_constant = is_constant
1604 && match state.global().lua_version {
1605 lua_types::LuaVersion::V51 => true,
1606 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 => binary,
1607 _ => false,
1608 };
1609 let (kind, name) = if suppress_constant {
1610 (None, None)
1611 } else {
1612 (kind, name)
1613 };
1614 typeerror_inner_parts(state, val, op, kind.as_deref(), name.as_deref())
1615}
1616
1617pub(crate) fn type_error_with_hint(
1623 state: &LuaState,
1624 val: &LuaValue,
1625 op: &[u8],
1626 kind: &[u8],
1627 name: &[u8],
1628) -> LuaError {
1629 let t = obj_type_name_static(val);
1630 let legacy_order = matches!(
1631 state.global().lua_version,
1632 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
1633 );
1634 let mut msg = Vec::new();
1635 msg.extend_from_slice(b"attempt to ");
1636 msg.extend_from_slice(op);
1637 if legacy_order {
1638 msg.extend_from_slice(b" ");
1639 msg.extend_from_slice(kind);
1640 msg.extend_from_slice(b" '");
1641 msg.extend_from_slice(name);
1642 msg.extend_from_slice(b"' (a ");
1643 msg.extend_from_slice(t);
1644 msg.extend_from_slice(b" value)");
1645 } else {
1646 msg.extend_from_slice(b" a ");
1647 msg.extend_from_slice(t);
1648 msg.extend_from_slice(b" value");
1649 msg.extend_from_slice(&format_var_info(Some(kind), Some(name)));
1650 }
1651 prefixed_runtime(state, msg)
1652}
1653
1654fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1657 match val {
1658 LuaValue::Nil => b"nil",
1659 LuaValue::Bool(_) => b"boolean",
1660 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1661 LuaValue::Str(_) => b"string",
1662 LuaValue::Table(_) => b"table",
1663 LuaValue::Function(_) => b"function",
1664 LuaValue::UserData(_) => b"userdata",
1665 LuaValue::LightUserData(_) => b"light userdata",
1666 LuaValue::Thread(_) => b"thread",
1667 }
1668}
1669
1670pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1679 let uses_callerror = matches!(
1680 state.global().lua_version,
1681 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
1682 );
1683 let (kind, name) = if uses_callerror {
1684 let ci_idx = state.current_ci_idx();
1685 let ci = state.get_ci(ci_idx).clone();
1686 let mut name: Option<Vec<u8>> = None;
1687 let kind = funcname_from_call(state, &ci, &mut name);
1688 if kind.is_some() {
1689 (kind.map(|k| k.to_vec()), name)
1690 } else {
1691 var_info_parts(state, val_idx)
1692 }
1693 } else {
1694 var_info_parts(state, val_idx)
1695 };
1696 typeerror_inner_parts(state, val, b"call", kind.as_deref(), name.as_deref())
1697}
1698
1699pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1702 if matches!(
1706 state.global().lua_version,
1707 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1708 ) {
1709 let mut msg = Vec::new();
1710 msg.extend_from_slice(b"'for' ");
1711 msg.extend_from_slice(what);
1712 msg.extend_from_slice(b" must be a number");
1713 return prefixed_runtime(state, msg);
1714 }
1715 let t = crate::tagmethods::obj_type_name(state, val)
1716 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1717 let mut msg = Vec::new();
1718 msg.extend_from_slice(b"bad 'for' ");
1719 msg.extend_from_slice(what);
1720 msg.extend_from_slice(b" (number expected, got ");
1721 msg.extend_from_slice(&t);
1722 msg.push(b')');
1723 prefixed_runtime(state, msg)
1724}
1725
1726pub(crate) fn op_int_error(
1730 state: &LuaState,
1731 p1: &LuaValue,
1732 p1_idx: StackIdx,
1733 p2: &LuaValue,
1734 p2_idx: StackIdx,
1735 msg: &[u8],
1736) -> LuaError {
1737 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1738 (p1, p1_idx)
1739 } else {
1740 (p2, p2_idx)
1741 };
1742 type_error(state, bad_val, bad_idx, msg)
1743}
1744
1745pub(crate) fn to_int_error(
1751 state: &LuaState,
1752 p1: &LuaValue,
1753 p1_idx: Option<StackIdx>,
1754 _p2: &LuaValue,
1755 p2_idx: Option<StackIdx>,
1756) -> LuaError {
1757 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1758 p1_idx
1759 } else {
1760 p2_idx
1761 };
1762 let extra = match bad_idx {
1763 Some(idx) => var_info(state, idx),
1764 None => Vec::new(),
1765 };
1766 let mut msg = Vec::new();
1767 msg.extend_from_slice(b"number");
1768 msg.extend_from_slice(&extra);
1769 msg.extend_from_slice(b" has no integer representation");
1770 prefixed_runtime(state, msg)
1771}
1772
1773pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1776 let t1 = state.obj_type_name(p1);
1777 let t2 = state.obj_type_name(p2);
1778 let msg = if t1 == t2 {
1779 let mut m = Vec::new();
1780 m.extend_from_slice(b"attempt to compare two ");
1781 m.extend_from_slice(&t1);
1782 m.extend_from_slice(b" values");
1783 m
1784 } else {
1785 let mut m = Vec::new();
1786 m.extend_from_slice(b"attempt to compare ");
1787 m.extend_from_slice(&t1);
1788 m.extend_from_slice(b" with ");
1789 m.extend_from_slice(&t2);
1790 m
1791 };
1792 prefixed_runtime(state, msg)
1793}
1794
1795pub(crate) fn add_info(
1804 _state: Option<&mut LuaState>,
1805 msg: &[u8],
1806 src: Option<&LuaString>,
1807 line: i32,
1808 unknown_line_as_question: bool,
1809) -> Vec<u8> {
1810 let mut buff = [0u8; LUA_IDSIZE];
1811 if let Some(src) = src {
1812 chunk_id(&mut buff, src.as_bytes(), src.len());
1813 } else if unknown_line_as_question {
1814 let mut out = Vec::with_capacity(5 + msg.len());
1815 out.extend_from_slice(b"?:?: ");
1816 out.extend_from_slice(msg);
1817 return out;
1818 } else {
1819 buff[0] = b'?';
1820 }
1821 let src_part = buff
1824 .iter()
1825 .position(|&b| b == 0)
1826 .map_or(&buff[..], |n| &buff[..n]);
1827 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1828 out.extend_from_slice(src_part);
1829 out.push(b':');
1830 let line_str = line.to_string();
1832 out.extend_from_slice(line_str.as_bytes());
1833 out.extend_from_slice(b": ");
1834 out.extend_from_slice(msg);
1835 out
1836}
1837
1838fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1843 if p.lineinfo.is_empty() {
1844 return false;
1845 }
1846
1847 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1848 let mut delta: i32 = 0;
1849 let mut pc = oldpc;
1850 loop {
1851 pc += 1;
1852 if pc as usize >= p.lineinfo.len() {
1853 break;
1854 }
1855 let lineinfo = p.lineinfo[pc as usize];
1856 if lineinfo == ABS_LINE_INFO {
1857 break;
1858 }
1859 delta += lineinfo as i32;
1860 if pc == newpc {
1861 return delta != 0;
1862 }
1863 }
1864 }
1865 get_func_line(p, oldpc) != get_func_line(p, newpc)
1866}
1867
1868pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1874 let ci_idx = state.current_ci_idx();
1875 let ci = state.get_ci(ci_idx).clone();
1876 state.get_ci_mut(ci_idx).set_trap(true);
1877 let proto = ci_lua_proto(&ci, state);
1878
1879 if ci.saved_pc() == 0 {
1880 if proto.is_vararg {
1881 return Ok(0);
1882 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1883 state.hook_call(ci_idx)?;
1884 }
1885 }
1886 Ok(1)
1887}
1888
1889pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
1898 let ci_idx = state.current_ci_idx();
1899 let ci = state.get_ci(ci_idx).clone();
1900
1901 let mask = state.hook_mask();
1902
1903 if !state.allowhook {
1904 return Ok(1);
1905 }
1906
1907 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
1908 state.get_ci_mut(ci_idx).set_trap(false);
1909 return Ok(0);
1910 }
1911
1912 let next_pc = pc + 1;
1913 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
1914
1915 let counthook = if mask & LUA_MASKCOUNT != 0 {
1916 let hc = state.hook_count() - 1;
1917 state.set_hook_count(hc);
1918 hc == 0
1919 } else {
1920 false
1921 };
1922
1923 if counthook {
1924 state.reset_hook_count();
1925 } else if mask & LUA_MASKLINE == 0 {
1926 return Ok(1);
1927 }
1928
1929 if counthook {
1934 if let Some(err) = state.sandbox_charge_interval() {
1935 return Err(err);
1936 }
1937 }
1938
1939 if ci.callstatus & CIST_HOOKYIELD != 0 {
1940 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
1941 return Ok(1);
1942 }
1943
1944 if state.ci_lua_closure(ci_idx).is_none() {
1945 return Ok(1);
1946 }
1947
1948 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
1949 if !cur_instr.is_in_top() {
1950 let ci_top = state.get_ci(ci_idx).top;
1951 state.set_top(ci_top);
1952 }
1953
1954 if counthook {
1955 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
1956 }
1957
1958 if mask & LUA_MASKLINE != 0 {
1959 let proto = ci_lua_proto(&ci, state);
1960 let oldpc = if state.old_pc() < proto.code.len() as u32 {
1961 state.old_pc() as i32
1962 } else {
1963 0
1964 };
1965 let npci = next_pc as i32 - 1;
1967
1968 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
1969 let newline = get_func_line(&proto, npci);
1970 state.call_hook_event(LUA_HOOKLINE, newline)?;
1971 }
1972 state.set_old_pc(npci as u32);
1973 }
1974
1975 if state.status() == lua_types::status::LuaStatus::Yield {
1976 if counthook {
1977 state.set_hook_count(1);
1978 }
1979 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
1980 return Err(LuaError::Yield);
1981 }
1982
1983 Ok(1)
1984}
1985
1986fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
1994 out.fill(0);
1995 let n = crate::object::chunk_id(&mut out[..], source);
1996 if n < out.len() {
1997 out[n] = 0;
1998 }
1999}
2000
2001fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
2005 crate::func::get_local_name(p, n, pc)
2006}
2007
2008fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
2010 get_local_name(&cl.proto, n, pc)
2011}
2012
2013fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
2020 match state.get_at(ci.func) {
2021 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
2022 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
2023 }
2024}