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 state.global().lua_version == lua_types::LuaVersion::V51 {
943 status = false;
944 } else if let Some(ci) = ci {
945 ar.istailcall = ci.callstatus & CIST_TAIL != 0;
946 ar.extraargs = ci.call_metamethods;
947 } else {
948 ar.istailcall = false;
949 ar.extraargs = 0;
950 }
951 }
952 b'n' => {
953 let mut name: Option<Vec<u8>> = None;
954 ar.namewhat = get_func_name(state, ci, &mut name);
955 if ar.namewhat.is_none() {
956 ar.namewhat = Some(b"");
957 ar.name = None;
958 } else {
959 ar.name = name;
960 }
961 }
962 b'r' => {
963 if matches!(
964 state.global().lua_version,
965 lua_types::LuaVersion::V51
966 | lua_types::LuaVersion::V52
967 | lua_types::LuaVersion::V53
968 ) {
969 status = false;
970 } else {
971 match ci {
972 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
973 ar.ftransfer = ci.transfer_ftransfer();
974 ar.ntransfer = ci.transfer_ntransfer();
975 }
976 _ => {
977 ar.ftransfer = 0;
978 ar.ntransfer = 0;
979 }
980 }
981 }
982 }
983 b'L' | b'f' => {}
984 _ => {
985 status = false;
986 }
987 }
988 }
989 status
990}
991
992pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
995 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
996 let func_val = state.peek_at(state.top_idx() - 1).clone();
997 state.pop_n(1);
998 debug_assert!(
999 matches!(func_val, LuaValue::Function(_)),
1000 "get_info: function expected"
1001 );
1002 let cl = match &func_val {
1003 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
1004 LuaValue::Function(c) => c.clone(),
1005 _ => unreachable!(),
1006 }),
1007 _ => None,
1008 };
1009 (cl, None, func_val, &what[1..])
1010 } else {
1011 let ci_idx = match ar.i_ci {
1012 Some(i) => i,
1013 None => return false,
1014 };
1015 if state.global().lua_version == lua_types::LuaVersion::V51
1016 && state.is_base_ci(ci_idx)
1017 {
1018 return get_info_tailcall_51(state, what, ar);
1019 }
1020 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
1021 debug_assert!(
1022 matches!(func_val, LuaValue::Function(_)),
1023 "get_info: non-function at ci->func"
1024 );
1025 let cl = match &func_val {
1026 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
1027 LuaValue::Function(c) => c.clone(),
1028 _ => unreachable!(),
1029 }),
1030 _ => None,
1031 };
1032 (cl, Some(ci_idx), func_val, what)
1033 };
1034
1035 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
1036 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
1037
1038 if what.contains(&b'f') {
1039 state.push(func_val);
1040 }
1041 if what.contains(&b'L') {
1042 let _ = collect_valid_lines(state, cl.as_ref());
1043 }
1044 status
1045}
1046
1047fn get_info_tailcall_51(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
1054 let what = if what.first() == Some(&b'>') {
1055 &what[1..]
1056 } else {
1057 what
1058 };
1059 info_tailcall(ar);
1060 let mut status = true;
1061 for &ch in what {
1062 if !matches!(ch, b'S' | b'l' | b'u' | b'n' | b't' | b'r' | b'L' | b'f') {
1063 status = false;
1064 }
1065 }
1066 if what.contains(&b'f') {
1067 state.push(LuaValue::Nil);
1068 }
1069 if what.contains(&b'L') {
1070 state.push(LuaValue::Nil);
1071 }
1072 status
1073}
1074
1075fn info_tailcall(ar: &mut LuaDebug) {
1077 ar.name = Some(Vec::new());
1078 ar.namewhat = Some(b"");
1079 ar.what = Some(b"tail");
1080 ar.linedefined = -1;
1081 ar.lastlinedefined = -1;
1082 ar.currentline = -1;
1083 ar.source = Some(b"=(tail call)".to_vec());
1084 ar.srclen = b"=(tail call)".len();
1085 chunk_id(&mut ar.short_src, b"=(tail call)", b"=(tail call)".len());
1086 ar.nups = 0;
1087 ar.istailcall = false;
1088}
1089
1090#[inline]
1096fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
1097 if pc < jmptarget {
1098 -1
1099 } else {
1100 pc
1101 }
1102}
1103
1104fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
1108 let mut setreg: i32 = -1;
1109 let mut jmptarget: i32 = 0;
1110
1111 let effective_lastpc = if p
1112 .code
1113 .get(lastpc as usize)
1114 .map_or(false, |i| i.is_mm_mode())
1115 {
1116 lastpc - 1
1117 } else {
1118 lastpc
1119 };
1120
1121 for pc in 0..effective_lastpc {
1122 let instr = p.code[pc as usize];
1123 let op = instr.opcode();
1124 let a = instr.arg_a() as i32;
1125
1126 let change = match op {
1127 OpCode::LoadNil => {
1128 let b = instr.arg_b() as i32;
1129 a <= reg && reg <= a + b
1130 }
1131 OpCode::TForCall => reg >= a + 2,
1132 OpCode::Call | OpCode::TailCall => reg >= a,
1133 OpCode::Jmp => {
1134 let b = instr.arg_s_j();
1135 let dest = pc + 1 + b;
1136 if dest <= effective_lastpc && dest > jmptarget {
1137 jmptarget = dest;
1138 }
1139 false
1140 }
1141 _ => {
1142 instr.test_a_mode() && reg == a
1143 }
1144 };
1145
1146 if change {
1147 setreg = filter_pc(pc, jmptarget);
1148 }
1149 }
1150 setreg
1151}
1152
1153fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
1158 match p.k.get(index) {
1159 Some(LuaValue::Str(s)) => {
1160 *name = s.as_bytes();
1161 Some(b"constant")
1162 }
1163 _ => {
1164 *name = b"?";
1165 None
1166 }
1167 }
1168}
1169
1170fn basic_get_obj_name<'a>(
1174 p: &'a LuaProto,
1175 ppc: &mut i32,
1176 reg: i32,
1177 name: &mut &'a [u8],
1178) -> Option<&'static [u8]> {
1179 let pc = *ppc;
1180 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
1182 *name = local_name;
1183 return Some(b"local");
1184 }
1185
1186 *ppc = find_set_reg(p, pc, reg);
1187 let pc = *ppc;
1188
1189 if pc == -1 {
1190 return None;
1191 }
1192
1193 let instr = p.code[pc as usize];
1194 let op = instr.opcode();
1195 match op {
1196 OpCode::Move => {
1197 let b = instr.arg_b() as i32;
1198 if b < instr.arg_a() as i32 {
1199 return basic_get_obj_name(p, ppc, b, name);
1200 }
1201 }
1202 OpCode::GetUpVal => {
1203 *name = upval_name(p, instr.arg_b() as usize);
1204 return Some(b"upvalue");
1205 }
1206 OpCode::LoadK => {
1207 return kname(p, instr.arg_bx() as usize, name);
1208 }
1209 OpCode::LoadKx => {
1210 let next = p.code[(pc + 1) as usize];
1211 return kname(p, next.arg_ax() as usize, name);
1212 }
1213 _ => {}
1214 }
1215 None
1216}
1217
1218fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
1222 let mut pc = pc;
1223 let what = basic_get_obj_name(p, &mut pc, c, name);
1224 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1225 *name = b"?";
1226 }
1227}
1228
1229fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1232 let c = instr.arg_c() as i32;
1233 if instr.arg_k() != 0 {
1234 kname(p, c as usize, name);
1235 } else {
1236 rname(p, pc, c, name);
1237 }
1238}
1239
1240fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1244 let t = instr.arg_b() as usize;
1245 let mut name: &[u8] = b"?";
1246 if isup {
1247 name = upval_name(p, t);
1248 } else {
1249 let mut pc = pc;
1250 let what = basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1251 if !matches!(what, Some(kind) if kind == b"local" || kind == b"upvalue") {
1252 name = b"?";
1253 }
1254 }
1255 if name == LUA_ENV {
1256 b"global"
1257 } else {
1258 b"field"
1259 }
1260}
1261
1262fn get_obj_name<'a>(
1266 p: &'a LuaProto,
1267 lastpc: i32,
1268 reg: i32,
1269 name: &mut &'a [u8],
1270) -> Option<&'static [u8]> {
1271 let mut lastpc = lastpc;
1272 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1273 if kind.is_some() {
1274 return kind;
1275 }
1276
1277 if lastpc == -1 {
1278 return None;
1279 }
1280
1281 let instr = p.code[lastpc as usize];
1282 let op = instr.opcode();
1283 match op {
1284 OpCode::GetTabUp => {
1285 let k = instr.arg_c() as usize;
1286 kname(p, k, name);
1287 Some(is_env(p, lastpc, instr, true))
1288 }
1289 OpCode::GetTable => {
1290 let k = instr.arg_c() as i32;
1291 rname(p, lastpc, k, name);
1292 Some(is_env(p, lastpc, instr, false))
1293 }
1294 OpCode::GetI => {
1295 *name = b"integer index";
1296 Some(b"field")
1297 }
1298 OpCode::GetField => {
1299 let k = instr.arg_c() as usize;
1300 kname(p, k, name);
1301 Some(is_env(p, lastpc, instr, false))
1302 }
1303 OpCode::Self_ => {
1304 rkname(p, lastpc, instr, name);
1305 Some(b"method")
1306 }
1307 _ => None,
1308 }
1309}
1310
1311fn funcname_from_code<'a>(
1318 state: &LuaState,
1319 p: &'a LuaProto,
1320 pc: i32,
1321 name: &mut Option<Vec<u8>>,
1322) -> Option<&'static [u8]> {
1323 let instr = p.code[pc as usize];
1324 let op = instr.opcode();
1325
1326 match op {
1327 OpCode::Call | OpCode::TailCall => {
1328 let mut name_bytes: &[u8] = b"?";
1329 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1330 *name = Some(name_bytes.to_vec());
1331 kind
1332 }
1333 OpCode::TForCall => {
1334 *name = Some(b"for iterator".to_vec());
1335 Some(b"for iterator")
1336 }
1337 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1339 get_tm_name(state, TagMethod::Index, name)
1340 }
1341 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1342 get_tm_name(state, TagMethod::NewIndex, name)
1343 }
1344 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1345 let tm_idx = instr.arg_c() as u8;
1346 let tm = TagMethod::from_u8(tm_idx);
1347 get_tm_name(state, tm, name)
1348 }
1349 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1350 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1351 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1352 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1353 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1354 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1355 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1356 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1357 _ => None,
1358 }
1359}
1360
1361fn get_tm_name(
1371 state: &LuaState,
1372 tm: TagMethod,
1373 name: &mut Option<Vec<u8>>,
1374) -> Option<&'static [u8]> {
1375 if state.global().lua_version == lua_types::LuaVersion::V51 {
1376 return None;
1377 }
1378 let raw_bytes: Vec<u8> = state
1381 .global()
1382 .tm_name(tm)
1383 .map(|s| s.as_bytes().to_vec())
1384 .unwrap_or_default();
1385 let keeps_prefix = matches!(
1386 state.global().lua_version,
1387 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1388 );
1389 let resolved = if keeps_prefix {
1390 raw_bytes
1391 } else {
1392 raw_bytes.strip_prefix(b"__").unwrap_or(&raw_bytes).to_vec()
1393 };
1394 *name = Some(resolved);
1395 Some(b"metamethod")
1396}
1397
1398fn funcname_from_call<'a>(
1401 state: &'a LuaState,
1402 ci: &CallInfo,
1403 name: &mut Option<Vec<u8>>,
1404) -> Option<&'static [u8]> {
1405 if ci.callstatus & CIST_HOOKED != 0 {
1406 *name = Some(b"?".to_vec());
1407 return Some(b"hook");
1408 }
1409 if ci.callstatus & CIST_FIN != 0 {
1410 *name = Some(b"__gc".to_vec());
1411 return Some(b"metamethod");
1412 }
1413 if ci.is_lua() {
1414 let proto = ci_lua_proto(ci, state);
1415 return funcname_from_code(state, &proto, current_pc(ci), name);
1416 }
1417 None
1418}
1419
1420fn in_stack(ci: &CallInfo, val_idx: StackIdx) -> i32 {
1429 let base = StackIdx(ci.func.0 + 1);
1430 let ci_top = ci.top;
1431 let mut pos = 0i32;
1432 let mut cur = base;
1433 while cur.0 < ci_top.0 {
1434 if cur == val_idx {
1435 return pos;
1436 }
1437 cur = StackIdx(cur.0 + 1);
1438 pos += 1;
1439 }
1440 -1
1441}
1442
1443fn get_upval_name(
1461 ci: &CallInfo,
1462 val_idx: StackIdx,
1463 name: &mut Vec<u8>,
1464 state: &LuaState,
1465) -> Option<&'static [u8]> {
1466 let proto = ci_lua_proto(ci, state);
1467 let lua_cl = match state.get_at(ci.func) {
1468 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1469 _ => return None,
1470 };
1471 let current_thread = state.cached_thread_id;
1472 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1473 let upval = upval_slot.get();
1474 if let Some((thread_id, idx)) = upval.try_open_payload() {
1475 if thread_id == current_thread && idx == val_idx {
1476 *name = upval_name(&proto, i).to_vec();
1477 return Some(b"upvalue");
1478 }
1479 }
1480 }
1481 None
1482}
1483
1484fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1489 match (kind, name) {
1490 (Some(k), Some(n)) => {
1491 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1492 out.extend_from_slice(b" (");
1493 out.extend_from_slice(k);
1494 out.extend_from_slice(b" '");
1495 out.extend_from_slice(n);
1496 out.extend_from_slice(b"')");
1497 out
1498 }
1499 _ => Vec::new(),
1500 }
1501}
1502
1503fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1507 let (kind, name) = var_info_parts(state, val_idx);
1508 format_var_info(kind.as_deref(), name.as_deref())
1509}
1510
1511fn var_info_parts(state: &LuaState, val_idx: StackIdx) -> (Option<Vec<u8>>, Option<Vec<u8>>) {
1518 let ci_idx = state.current_ci_idx();
1519 let ci = state.get_ci(ci_idx).clone();
1520 let mut kind: Option<&[u8]> = None;
1521 let mut name_owned: Vec<u8> = b"?".to_vec();
1522
1523 if ci.is_lua() {
1524 let mut up_name: Vec<u8> = b"?".to_vec();
1525 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1526 if kind.is_some() {
1527 name_owned = up_name;
1528 } else {
1529 let reg = in_stack(&ci, val_idx);
1530 if reg >= 0 {
1531 let proto = ci_lua_proto(&ci, state);
1532 let mut nref: &[u8] = b"?";
1533 let pc = current_pc(&ci);
1534 let k = get_obj_name(&proto, pc, reg, &mut nref);
1535 kind = k;
1536 if kind.is_some() {
1537 name_owned = nref.to_vec();
1538 }
1539 }
1540 }
1541 }
1542 match kind {
1543 Some(k) => (Some(k.to_vec()), Some(name_owned)),
1544 None => (None, None),
1545 }
1546}
1547
1548fn typeerror_inner_parts(
1559 state: &LuaState,
1560 val: &LuaValue,
1561 op: &[u8],
1562 kind: Option<&[u8]>,
1563 name: Option<&[u8]>,
1564) -> LuaError {
1565 let t = state.obj_type_name(val);
1566 let legacy_order = matches!(
1567 state.global().lua_version,
1568 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
1569 );
1570 let mut msg = Vec::new();
1571 msg.extend_from_slice(b"attempt to ");
1572 msg.extend_from_slice(op);
1573 if let (true, Some(k), Some(n)) = (legacy_order, kind, name) {
1574 msg.extend_from_slice(b" ");
1575 msg.extend_from_slice(k);
1576 msg.extend_from_slice(b" '");
1577 msg.extend_from_slice(n);
1578 msg.extend_from_slice(b"' (a ");
1579 msg.extend_from_slice(&t);
1580 msg.extend_from_slice(b" value)");
1581 } else {
1582 msg.extend_from_slice(b" a ");
1583 msg.extend_from_slice(&t);
1584 msg.extend_from_slice(b" value");
1585 msg.extend_from_slice(&format_var_info(kind, name));
1586 }
1587 prefixed_runtime(state, msg)
1588}
1589
1590pub(crate) fn type_error(
1594 state: &LuaState,
1595 val: &LuaValue,
1596 val_idx: StackIdx,
1597 op: &[u8],
1598) -> LuaError {
1599 let (kind, name) = var_info_parts(state, val_idx);
1600 typeerror_inner_parts(state, val, op, kind.as_deref(), name.as_deref())
1601}
1602
1603pub(crate) fn arith_type_error(
1618 state: &LuaState,
1619 val: &LuaValue,
1620 val_idx: StackIdx,
1621 op: &[u8],
1622 binary: bool,
1623) -> LuaError {
1624 let (kind, name) = var_info_parts(state, val_idx);
1625 let is_constant = matches!(kind.as_deref(), Some(b"constant"));
1626 let suppress_constant = is_constant
1627 && match state.global().lua_version {
1628 lua_types::LuaVersion::V51 => true,
1629 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 => binary,
1630 _ => false,
1631 };
1632 let (kind, name) = if suppress_constant {
1633 (None, None)
1634 } else {
1635 (kind, name)
1636 };
1637 typeerror_inner_parts(state, val, op, kind.as_deref(), name.as_deref())
1638}
1639
1640pub(crate) fn type_error_with_hint(
1646 state: &LuaState,
1647 val: &LuaValue,
1648 op: &[u8],
1649 kind: &[u8],
1650 name: &[u8],
1651) -> LuaError {
1652 let t = obj_type_name_static(val);
1653 let legacy_order = matches!(
1654 state.global().lua_version,
1655 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
1656 );
1657 let mut msg = Vec::new();
1658 msg.extend_from_slice(b"attempt to ");
1659 msg.extend_from_slice(op);
1660 if legacy_order {
1661 msg.extend_from_slice(b" ");
1662 msg.extend_from_slice(kind);
1663 msg.extend_from_slice(b" '");
1664 msg.extend_from_slice(name);
1665 msg.extend_from_slice(b"' (a ");
1666 msg.extend_from_slice(t);
1667 msg.extend_from_slice(b" value)");
1668 } else {
1669 msg.extend_from_slice(b" a ");
1670 msg.extend_from_slice(t);
1671 msg.extend_from_slice(b" value");
1672 msg.extend_from_slice(&format_var_info(Some(kind), Some(name)));
1673 }
1674 prefixed_runtime(state, msg)
1675}
1676
1677fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1680 match val {
1681 LuaValue::Nil => b"nil",
1682 LuaValue::Bool(_) => b"boolean",
1683 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1684 LuaValue::Str(_) => b"string",
1685 LuaValue::Table(_) => b"table",
1686 LuaValue::Function(_) => b"function",
1687 LuaValue::UserData(_) => b"userdata",
1688 LuaValue::LightUserData(_) => b"light userdata",
1689 LuaValue::Thread(_) => b"thread",
1690 }
1691}
1692
1693pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1702 let uses_callerror = matches!(
1703 state.global().lua_version,
1704 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
1705 );
1706 let (kind, name) = if uses_callerror {
1707 let ci_idx = state.current_ci_idx();
1708 let ci = state.get_ci(ci_idx).clone();
1709 let mut name: Option<Vec<u8>> = None;
1710 let kind = funcname_from_call(state, &ci, &mut name);
1711 if kind.is_some() {
1712 (kind.map(|k| k.to_vec()), name)
1713 } else {
1714 var_info_parts(state, val_idx)
1715 }
1716 } else {
1717 var_info_parts(state, val_idx)
1718 };
1719 typeerror_inner_parts(state, val, b"call", kind.as_deref(), name.as_deref())
1720}
1721
1722pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1725 if matches!(
1729 state.global().lua_version,
1730 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1731 ) {
1732 let mut msg = Vec::new();
1733 msg.extend_from_slice(b"'for' ");
1734 msg.extend_from_slice(what);
1735 msg.extend_from_slice(b" must be a number");
1736 return prefixed_runtime(state, msg);
1737 }
1738 let t = crate::tagmethods::obj_type_name(state, val)
1739 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1740 let mut msg = Vec::new();
1741 msg.extend_from_slice(b"bad 'for' ");
1742 msg.extend_from_slice(what);
1743 msg.extend_from_slice(b" (number expected, got ");
1744 msg.extend_from_slice(&t);
1745 msg.push(b')');
1746 prefixed_runtime(state, msg)
1747}
1748
1749pub(crate) fn op_int_error(
1753 state: &LuaState,
1754 p1: &LuaValue,
1755 p1_idx: StackIdx,
1756 p2: &LuaValue,
1757 p2_idx: StackIdx,
1758 msg: &[u8],
1759) -> LuaError {
1760 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1761 (p1, p1_idx)
1762 } else {
1763 (p2, p2_idx)
1764 };
1765 type_error(state, bad_val, bad_idx, msg)
1766}
1767
1768pub(crate) fn to_int_error(
1774 state: &LuaState,
1775 p1: &LuaValue,
1776 p1_idx: Option<StackIdx>,
1777 _p2: &LuaValue,
1778 p2_idx: Option<StackIdx>,
1779) -> LuaError {
1780 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1781 p1_idx
1782 } else {
1783 p2_idx
1784 };
1785 let extra = match bad_idx {
1786 Some(idx) => var_info(state, idx),
1787 None => Vec::new(),
1788 };
1789 let mut msg = Vec::new();
1790 msg.extend_from_slice(b"number");
1791 msg.extend_from_slice(&extra);
1792 msg.extend_from_slice(b" has no integer representation");
1793 prefixed_runtime(state, msg)
1794}
1795
1796pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1799 let t1 = state.obj_type_name(p1);
1800 let t2 = state.obj_type_name(p2);
1801 let msg = if t1 == t2 {
1802 let mut m = Vec::new();
1803 m.extend_from_slice(b"attempt to compare two ");
1804 m.extend_from_slice(&t1);
1805 m.extend_from_slice(b" values");
1806 m
1807 } else {
1808 let mut m = Vec::new();
1809 m.extend_from_slice(b"attempt to compare ");
1810 m.extend_from_slice(&t1);
1811 m.extend_from_slice(b" with ");
1812 m.extend_from_slice(&t2);
1813 m
1814 };
1815 prefixed_runtime(state, msg)
1816}
1817
1818pub(crate) fn add_info(
1827 _state: Option<&mut LuaState>,
1828 msg: &[u8],
1829 src: Option<&LuaString>,
1830 line: i32,
1831 unknown_line_as_question: bool,
1832) -> Vec<u8> {
1833 let mut buff = [0u8; LUA_IDSIZE];
1834 if let Some(src) = src {
1835 chunk_id(&mut buff, src.as_bytes(), src.len());
1836 } else if unknown_line_as_question {
1837 let mut out = Vec::with_capacity(5 + msg.len());
1838 out.extend_from_slice(b"?:?: ");
1839 out.extend_from_slice(msg);
1840 return out;
1841 } else {
1842 buff[0] = b'?';
1843 }
1844 let src_part = buff
1847 .iter()
1848 .position(|&b| b == 0)
1849 .map_or(&buff[..], |n| &buff[..n]);
1850 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1851 out.extend_from_slice(src_part);
1852 out.push(b':');
1853 let line_str = line.to_string();
1855 out.extend_from_slice(line_str.as_bytes());
1856 out.extend_from_slice(b": ");
1857 out.extend_from_slice(msg);
1858 out
1859}
1860
1861fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1866 if p.lineinfo.is_empty() {
1867 return false;
1868 }
1869
1870 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1871 let mut delta: i32 = 0;
1872 let mut pc = oldpc;
1873 loop {
1874 pc += 1;
1875 if pc as usize >= p.lineinfo.len() {
1876 break;
1877 }
1878 let lineinfo = p.lineinfo[pc as usize];
1879 if lineinfo == ABS_LINE_INFO {
1880 break;
1881 }
1882 delta += lineinfo as i32;
1883 if pc == newpc {
1884 return delta != 0;
1885 }
1886 }
1887 }
1888 get_func_line(p, oldpc) != get_func_line(p, newpc)
1889}
1890
1891pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1897 let ci_idx = state.current_ci_idx();
1898 let ci = state.get_ci(ci_idx).clone();
1899 state.get_ci_mut(ci_idx).set_trap(true);
1900 let proto = ci_lua_proto(&ci, state);
1901
1902 if ci.saved_pc() == 0 {
1903 if proto.is_vararg {
1904 return Ok(0);
1905 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1906 state.hook_call(ci_idx)?;
1907 }
1908 }
1909 Ok(1)
1910}
1911
1912pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
1921 let ci_idx = state.current_ci_idx();
1922 let ci = state.get_ci(ci_idx).clone();
1923
1924 let mask = state.hook_mask();
1925
1926 if !state.allowhook {
1927 return Ok(1);
1928 }
1929
1930 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
1931 state.get_ci_mut(ci_idx).set_trap(false);
1932 return Ok(0);
1933 }
1934
1935 let next_pc = pc + 1;
1936 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
1937
1938 let counthook = if mask & LUA_MASKCOUNT != 0 {
1939 let hc = state.hook_count() - 1;
1940 state.set_hook_count(hc);
1941 hc == 0
1942 } else {
1943 false
1944 };
1945
1946 if counthook {
1947 state.reset_hook_count();
1948 } else if mask & LUA_MASKLINE == 0 {
1949 return Ok(1);
1950 }
1951
1952 if counthook {
1957 if let Some(err) = state.sandbox_charge_interval() {
1958 return Err(err);
1959 }
1960 }
1961
1962 if ci.callstatus & CIST_HOOKYIELD != 0 {
1963 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
1964 return Ok(1);
1965 }
1966
1967 if state.ci_lua_closure(ci_idx).is_none() {
1968 return Ok(1);
1969 }
1970
1971 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
1972 if !cur_instr.is_in_top() {
1973 let ci_top = state.get_ci(ci_idx).top;
1974 state.set_top(ci_top);
1975 }
1976
1977 if counthook {
1978 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
1979 }
1980
1981 if mask & LUA_MASKLINE != 0 {
1982 let proto = ci_lua_proto(&ci, state);
1983 let oldpc = if state.old_pc() < proto.code.len() as u32 {
1984 state.old_pc() as i32
1985 } else {
1986 0
1987 };
1988 let npci = next_pc as i32 - 1;
1990
1991 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
1992 let newline = get_func_line(&proto, npci);
1993 state.call_hook_event(LUA_HOOKLINE, newline)?;
1994 }
1995 state.set_old_pc(npci as u32);
1996 }
1997
1998 if state.status() == lua_types::status::LuaStatus::Yield {
1999 if counthook {
2000 state.set_hook_count(1);
2001 }
2002 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
2003 return Err(LuaError::Yield);
2004 }
2005
2006 Ok(1)
2007}
2008
2009fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
2017 out.fill(0);
2018 let n = crate::object::chunk_id(&mut out[..], source);
2019 if n < out.len() {
2020 out[n] = 0;
2021 }
2022}
2023
2024fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
2028 crate::func::get_local_name(p, n, pc)
2029}
2030
2031fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
2033 get_local_name(&cl.proto, n, pc)
2034}
2035
2036fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
2043 match state.get_at(ci.func) {
2044 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
2045 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
2046 }
2047}