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
217fn arg_error_global_name(
243 state: &LuaState,
244 ar: &LuaDebug,
245 version: lua_types::LuaVersion,
246) -> Option<Vec<u8>> {
247 if version == lua_types::LuaVersion::V51 {
248 return None;
249 }
250 let keeps_global_prefix = version == lua_types::LuaVersion::V52;
251 let ci_idx = ar.i_ci?;
252 let func_slot = state.get_ci(ci_idx).func;
253 let func_val = state.get_at(func_slot).clone();
254 let found = find_func_name_in_loaded(state, &func_val)?;
255 if !keeps_global_prefix && found.starts_with(b"_G.") {
256 Some(found[3..].to_vec())
257 } else {
258 Some(found)
259 }
260}
261
262pub fn arg_error_impl(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> LuaError {
266 let mut ar = LuaDebug::default();
267 if !get_stack(state, 0, &mut ar) {
268 let msg = format!(
269 "bad argument #{} ({})",
270 arg,
271 String::from_utf8_lossy(extramsg)
272 );
273 return c_api_runtime(state, msg.into_bytes());
274 }
275 get_info(state, b"n", &mut ar);
276 if ar.namewhat.as_deref() == Some(b"method") {
277 arg -= 1;
278 if arg == 0 {
279 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
280 let msg = format!(
281 "calling '{}' on bad self ({})",
282 String::from_utf8_lossy(&name),
283 String::from_utf8_lossy(extramsg)
284 );
285 return c_api_runtime(state, msg.into_bytes());
286 }
287 }
288 let version = state.global().lua_version;
289 let fname = ar
290 .name
291 .clone()
292 .or_else(|| arg_error_global_name(state, &ar, version))
293 .unwrap_or_else(|| b"?".to_vec());
294 let msg = format!(
295 "bad argument #{} to '{}' ({})",
296 arg,
297 String::from_utf8_lossy(&fname),
298 String::from_utf8_lossy(extramsg)
299 );
300 c_api_runtime(state, msg.into_bytes())
301}
302
303pub struct LuaDebug {
314 pub event: i32,
315 pub name: Option<Vec<u8>>,
316 pub namewhat: Option<&'static [u8]>,
317 pub what: Option<&'static [u8]>,
318 pub source: Option<Vec<u8>>,
319 pub srclen: usize,
320 pub currentline: i32,
321 pub linedefined: i32,
322 pub lastlinedefined: i32,
323 pub nups: u8,
324 pub nparams: u8,
325 pub isvararg: bool,
326 pub istailcall: bool,
327 pub extraargs: u8,
328 pub ftransfer: u16,
329 pub ntransfer: u16,
330 pub short_src: [u8; LUA_IDSIZE],
331 pub i_ci: Option<CallInfoIdx>,
333}
334
335impl Default for LuaDebug {
336 fn default() -> Self {
337 LuaDebug {
338 event: 0,
339 name: None,
340 namewhat: None,
341 what: None,
342 source: None,
343 srclen: 0,
344 currentline: -1,
345 linedefined: -1,
346 lastlinedefined: -1,
347 nups: 0,
348 nparams: 0,
349 isvararg: false,
350 istailcall: false,
351 extraargs: 0,
352 ftransfer: 0,
353 ntransfer: 0,
354 short_src: [0u8; LUA_IDSIZE],
355 i_ci: None,
356 }
357 }
358}
359
360#[inline]
364fn is_lua_closure(cl: Option<&LuaClosure>) -> bool {
365 matches!(cl, Some(LuaClosure::Lua(_)))
366}
367
368fn current_pc(ci: &CallInfo) -> i32 {
383 debug_assert!(ci.is_lua());
384 ci.saved_pc().saturating_sub(1) as i32
387}
388
389fn get_baseline(f: &LuaProto, pc: i32, basepc: &mut i32) -> i32 {
397 if f.abslineinfo.is_empty() || pc < f.abslineinfo[0].pc {
398 *basepc = -1;
399 return f.linedefined;
400 }
401 let mut i = (pc as u32 / MAX_IWTH_ABS as u32).saturating_sub(1) as usize;
403 debug_assert!(
404 i < f.abslineinfo.len() && f.abslineinfo[i].pc <= pc,
405 "getbaseline: estimate is not a lower bound"
406 );
407 while i + 1 < f.abslineinfo.len() && pc >= f.abslineinfo[i + 1].pc {
408 i += 1;
409 }
410 *basepc = f.abslineinfo[i].pc;
411 f.abslineinfo[i].line
412}
413
414pub(crate) fn get_func_line(f: &LuaProto, pc: i32) -> i32 {
418 if f.lineinfo.is_empty() {
419 return -1;
420 }
421 let mut basepc: i32 = 0;
422 let mut baseline = get_baseline(f, pc, &mut basepc);
423 while basepc < pc {
426 basepc += 1;
427 debug_assert!(
428 f.lineinfo[basepc as usize] != ABS_LINE_INFO,
429 "get_func_line: hit ABSLINEINFO in incremental walk"
430 );
431 baseline += f.lineinfo[basepc as usize] as i32;
432 }
433 baseline
434}
435
436fn get_current_line(ci: &CallInfo, state: &LuaState) -> i32 {
439 let proto = ci_lua_proto(ci, state);
440 get_func_line(&proto, current_pc(ci))
441}
442
443pub(crate) fn arm_traps(state: &mut LuaState) {
455 set_traps(state);
456}
457
458fn set_traps(state: &mut LuaState) {
459 for ci in state.call_stack_mut().iter_mut() {
463 if ci.is_lua() {
464 ci.set_trap(true);
465 }
466 }
467}
468
469pub fn set_hook(
472 state: &mut LuaState,
473 func: Option<Box<dyn FnMut(&mut LuaState, &LuaDebug)>>,
474 mask: i32,
475 count: i32,
476) {
477 let (func, mask) = if func.is_none() || mask == 0 {
478 (None, 0i32)
479 } else {
480 (func, mask)
481 };
482 state.set_hook(func);
483 state.set_base_hook_count(count);
484 state.reset_hook_count();
486 state.set_hook_mask(mask as u8);
488 if mask != 0 {
489 set_traps(state);
490 }
491}
492
493pub fn get_hook_installed(state: &LuaState) -> bool {
500 state.hook().is_some()
501}
502
503pub fn get_hook_mask(state: &LuaState) -> i32 {
506 state.hook_mask() as i32
507}
508
509pub fn get_hook_count(state: &LuaState) -> i32 {
512 state.base_hook_count()
513}
514
515pub fn get_stack(state: &LuaState, level: i32, ar: &mut LuaDebug) -> bool {
522 if level < 0 {
523 return false;
524 }
525 if state.global().lua_version == lua_types::LuaVersion::V51 {
526 return get_stack_51(state, level, ar);
527 }
528 let mut remaining = level;
529 let mut ci_idx = state.current_ci_idx();
530 loop {
531 if remaining == 0 {
532 break;
533 }
534 match state.prev_ci(ci_idx) {
535 Some(prev) => {
536 ci_idx = prev;
537 remaining -= 1;
538 }
539 None => {
540 return false;
541 }
542 }
543 }
544 if !state.is_base_ci(ci_idx) {
545 ar.i_ci = Some(ci_idx);
546 true
547 } else {
548 false
549 }
550}
551
552fn get_stack_51(state: &LuaState, level: i32, ar: &mut LuaDebug) -> bool {
574 let mut remaining = level;
575 let mut ci_idx = state.current_ci_idx();
576 loop {
577 if remaining <= 0 || state.is_base_ci(ci_idx) {
578 break;
579 }
580 remaining -= 1;
581 let ci = state.get_ci(ci_idx);
582 if ci.is_lua() {
583 remaining -= ci.tailcalls as i32;
584 }
585 match state.prev_ci(ci_idx) {
586 Some(prev) => ci_idx = prev,
587 None => break,
588 }
589 }
590 if remaining == 0 && !state.is_base_ci(ci_idx) {
591 ar.i_ci = Some(ci_idx);
592 true
593 } else if remaining < 0 {
594 ar.i_ci = Some(CallInfoIdx(0));
595 true
596 } else {
597 false
598 }
599}
600
601fn visible_upvalue_count_51(p: &LuaProto) -> usize {
612 p.upvalues
613 .iter()
614 .filter(|uv| uv.name.as_ref().map_or(true, |s| s.as_bytes() != LUA_ENV))
615 .count()
616}
617
618fn upval_name(p: &LuaProto, uv: usize) -> &[u8] {
621 debug_assert!(uv < p.upvalues.len(), "upval_name: index out of range");
624 p.upvalues[uv]
627 .name
628 .as_ref()
629 .map_or(b"?" as &[u8], |s| s.as_bytes())
630}
631
632fn temporary_local_name(state: &LuaState, ci_is_lua: bool) -> &'static [u8] {
641 match state.global().lua_version {
642 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 => {
643 b"(*temporary)"
644 }
645 _ => {
646 if ci_is_lua {
647 b"(temporary)"
648 } else {
649 b"(C temporary)"
650 }
651 }
652 }
653}
654
655fn find_vararg(state: &LuaState, ci: &CallInfo, n: i32) -> Option<(StackIdx, &'static [u8])> {
666 let proto = ci_lua_proto(ci, state);
667 if proto.is_vararg {
668 let nextra = ci.nextra_args();
669 if n >= -(nextra as i32) {
670 let pos = ci.func - (nextra + n + 1);
673 let name: &'static [u8] = match state.global().lua_version {
674 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 => b"(*vararg)",
675 _ => b"(vararg)",
676 };
677 return Some((pos, name));
678 }
679 }
680 None
681}
682
683pub(crate) fn find_local(
696 state: &LuaState,
697 ci_idx: CallInfoIdx,
698 n: i32,
699 pos: Option<&mut StackIdx>,
700) -> Option<Vec<u8>> {
701 let ci = state.get_ci(ci_idx);
702 let base = ci.func + 1;
703 let mut name: Option<Vec<u8>> = None;
704
705 if ci.is_lua() {
706 if n < 0 {
707 if let Some((vpos, vname)) = find_vararg(state, ci, n) {
708 if let Some(out_pos) = pos {
709 *out_pos = vpos;
710 }
711 return Some(vname.to_vec());
712 }
713 return None;
714 } else {
715 let proto = ci_lua_proto(ci, state);
716 let pc = current_pc(ci);
717 name = crate::func::get_local_name(&proto, n, pc).map(|s| s.to_vec());
718 }
719 }
720
721 if name.is_none() {
722 let limit: u32 = if ci_idx == state.current_ci_idx() {
723 state.top_idx().0
724 } else {
725 ci.next
726 .map(|next| state.get_ci(next).func.0)
727 .unwrap_or_else(|| state.top_idx().0)
728 };
729 if n > 0 && limit.saturating_sub(base.0) >= n as u32 {
730 name = Some(temporary_local_name(state, ci.is_lua()).to_vec());
731 } else {
732 return None;
733 }
734 }
735
736 if let Some(out_pos) = pos {
737 *out_pos = base + (n - 1);
738 }
739 name
740}
741
742pub fn get_local(state: &mut LuaState, ar: Option<&LuaDebug>, n: i32) -> Option<Vec<u8>> {
747 if ar.is_none() {
748 let top_val = state.peek_top();
750 if !matches!(top_val, LuaValue::Function(LuaClosure::Lua(_))) {
751 return None;
752 }
753 let name_owned: Option<Vec<u8>> = {
756 let cl = match top_val {
757 LuaValue::Function(LuaClosure::Lua(ref cl)) => cl.clone(),
758 _ => unreachable!(),
759 };
760 get_local_name_from_closure(&cl, n, 0).map(|s| s.to_vec())
762 };
763 return name_owned;
764 }
765
766 let ar = ar.unwrap();
767 let ci_idx = ar.i_ci?;
768 let mut pos = StackIdx(0);
769 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
772
773 if name_owned.is_some() {
774 let val = state.get_at(pos).clone();
775 state.push(val);
776 }
777 name_owned
778}
779
780pub fn set_local(state: &mut LuaState, ar: &LuaDebug, n: i32) -> Option<Vec<u8>> {
784 let ci_idx = ar.i_ci?;
785 let mut pos = StackIdx(0);
786 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
788 if name_owned.is_some() {
789 let val = state.get_at(state.top_idx() - 1).clone();
790 state.set_at(pos, val);
791 state.pop_n(1);
792 }
793 name_owned
794}
795
796fn func_info(ar: &mut LuaDebug, cl: Option<&LuaClosure>) {
801 if !is_lua_closure(cl) {
802 ar.source = Some(b"=[C]".to_vec());
804 ar.srclen = b"=[C]".len();
805 ar.linedefined = -1;
806 ar.lastlinedefined = -1;
807 ar.what = Some(b"C");
808 } else {
809 let lua_cl = match cl {
810 Some(LuaClosure::Lua(cl)) => cl,
811 _ => unreachable!(),
812 };
813 let proto: &LuaProto = &lua_cl.proto;
815 if let Some(src) = proto.source_string() {
817 ar.source = Some(src.as_bytes().to_vec());
818 ar.srclen = src.as_bytes().len();
819 } else {
820 ar.source = Some(b"=?".to_vec());
821 ar.srclen = b"=?".len();
822 }
823 ar.linedefined = proto.linedefined;
824 ar.lastlinedefined = proto.lastlinedefined;
825 ar.what = Some(if ar.linedefined == 0 { b"main" } else { b"Lua" });
826 }
827 chunk_id(
829 &mut ar.short_src,
830 ar.source.as_deref().unwrap_or(b"?"),
831 ar.srclen,
832 );
833}
834
835fn next_line(p: &LuaProto, currentline: i32, pc: usize) -> i32 {
839 if p.lineinfo.get(pc).copied() != Some(ABS_LINE_INFO) {
841 currentline + p.lineinfo[pc] as i32
842 } else {
843 get_func_line(p, pc as i32)
844 }
845}
846
847fn collect_valid_lines(state: &mut LuaState, cl: Option<&LuaClosure>) -> Result<(), LuaError> {
851 if !is_lua_closure(cl) {
852 state.push(LuaValue::Nil);
854 return Ok(());
855 }
856 let lua_cl = match cl {
857 Some(LuaClosure::Lua(cl)) => cl.clone(),
858 _ => unreachable!(),
859 };
860 let proto: GcRef<LuaProto> = lua_cl.proto.clone();
862 let p: &LuaProto = &proto;
863
864 let mut currentline = p.linedefined;
865
866 let t = state.new_table();
868 state.push(LuaValue::Table(t.clone()));
870
871 if !p.lineinfo.is_empty() {
872 let v = LuaValue::Bool(true);
874
875 let start_i = if !p.is_vararg {
876 0usize
877 } else {
878 debug_assert!(
880 p.code.first().map(|i| i.is_vararg_prep()).unwrap_or(false),
881 "collect_valid_lines: first instruction of vararg should be OP_VARARGPREP"
882 );
883 currentline = next_line(p, currentline, 0);
884 1usize
885 };
886
887 for i in start_i..p.lineinfo.len() {
889 currentline = next_line(p, currentline, i);
890 t.raw_set_int(state, currentline as i64, v.clone())?;
892 }
893 }
894 Ok(())
895}
896
897fn get_func_name<'a>(
913 state: &'a LuaState,
914 ci: Option<&CallInfo>,
915 name: &mut Option<Vec<u8>>,
916) -> Option<&'static [u8]> {
917 let ci = ci?;
918 match state.global().lua_version {
919 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 => {
920 if ci.callstatus & CIST_TAIL != 0 {
921 return None;
922 }
923 funcname_from_caller_code(state, ci, false, name)
924 }
925 lua_types::LuaVersion::V53 => {
926 if ci.callstatus & CIST_FIN != 0 {
927 *name = Some(b"__gc".to_vec());
928 return Some(b"metamethod");
929 }
930 if ci.callstatus & CIST_TAIL != 0 {
931 return None;
932 }
933 funcname_from_caller_code(state, ci, true, name)
934 }
935 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 | _ => {
936 if ci.callstatus & CIST_TAIL != 0 {
937 return None;
938 }
939 let prev_ci = state.get_ci(ci.previous?).clone();
940 funcname_from_call(state, &prev_ci, name)
941 }
942 }
943}
944
945fn funcname_from_caller_code<'a>(
953 state: &'a LuaState,
954 ci: &CallInfo,
955 check_hooked: bool,
956 name: &mut Option<Vec<u8>>,
957) -> Option<&'static [u8]> {
958 let prev_ci = state.get_ci(ci.previous?).clone();
959 if !prev_ci.is_lua() {
960 return None;
961 }
962 if check_hooked && prev_ci.callstatus & CIST_HOOKED != 0 {
963 *name = Some(b"?".to_vec());
964 return Some(b"hook");
965 }
966 let proto = ci_lua_proto(&prev_ci, state);
967 funcname_from_code(state, &proto, current_pc(&prev_ci), name)
968}
969
970fn aux_get_info(
973 state: &LuaState,
974 what: &[u8],
975 ar: &mut LuaDebug,
976 cl: Option<&LuaClosure>,
977 ci: Option<&CallInfo>,
978) -> bool {
979 let mut status = true;
980 for &ch in what {
981 match ch {
982 b'S' => {
983 func_info(ar, cl);
984 }
985 b'l' => {
986 ar.currentline = match ci {
987 Some(ci) if ci.is_lua() => get_current_line(ci, state),
988 _ => -1,
989 };
990 }
991 b'u' => {
992 ar.nups = cl.map_or(0, |c| c.nupvalues() as u8);
993 match cl {
994 Some(LuaClosure::Lua(lua_cl)) => {
995 ar.isvararg = lua_cl.proto.is_vararg;
997 ar.nparams = lua_cl.proto.numparams;
998 if state.global().lua_version == lua_types::LuaVersion::V51 {
999 ar.nups = visible_upvalue_count_51(&lua_cl.proto) as u8;
1000 }
1001 }
1002 _ => {
1003 ar.isvararg = true;
1004 ar.nparams = 0;
1005 }
1006 }
1007 }
1008 b't' => {
1009 if let Some(ci) = ci {
1010 ar.istailcall = ci.callstatus & CIST_TAIL != 0;
1011 ar.extraargs = ci.call_metamethods;
1012 } else {
1013 ar.istailcall = false;
1014 ar.extraargs = 0;
1015 }
1016 }
1017 b'n' => {
1018 let mut name: Option<Vec<u8>> = None;
1019 ar.namewhat = get_func_name(state, ci, &mut name);
1020 if ar.namewhat.is_none() {
1021 ar.namewhat = Some(b"");
1022 ar.name = None;
1023 } else {
1024 ar.name = name;
1025 }
1026 }
1027 b'r' => match ci {
1029 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
1030 ar.ftransfer = ci.transfer_ftransfer();
1032 ar.ntransfer = ci.transfer_ntransfer();
1033 }
1034 _ => {
1035 ar.ftransfer = 0;
1036 ar.ntransfer = 0;
1037 }
1038 },
1039 b'L' | b'f' => {}
1040 _ => {
1041 status = false;
1042 }
1043 }
1044 }
1045 status
1046}
1047
1048pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
1051 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
1052 let func_val = state.peek_at(state.top_idx() - 1).clone();
1053 state.pop_n(1);
1054 debug_assert!(
1055 matches!(func_val, LuaValue::Function(_)),
1056 "get_info: function expected"
1057 );
1058 let cl = match &func_val {
1059 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
1060 LuaValue::Function(c) => c.clone(),
1061 _ => unreachable!(),
1062 }),
1063 _ => None,
1064 };
1065 (cl, None, func_val, &what[1..])
1066 } else {
1067 let ci_idx = match ar.i_ci {
1068 Some(i) => i,
1069 None => return false,
1070 };
1071 if state.global().lua_version == lua_types::LuaVersion::V51
1072 && state.is_base_ci(ci_idx)
1073 {
1074 return get_info_tailcall_51(state, what, ar);
1075 }
1076 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
1077 debug_assert!(
1078 matches!(func_val, LuaValue::Function(_)),
1079 "get_info: non-function at ci->func"
1080 );
1081 let cl = match &func_val {
1082 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
1083 LuaValue::Function(c) => c.clone(),
1084 _ => unreachable!(),
1085 }),
1086 _ => None,
1087 };
1088 (cl, Some(ci_idx), func_val, what)
1089 };
1090
1091 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
1092 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
1093
1094 if what.contains(&b'f') {
1095 state.push(func_val);
1096 }
1097 if what.contains(&b'L') {
1098 let _ = collect_valid_lines(state, cl.as_ref());
1100 }
1101 status
1102}
1103
1104fn get_info_tailcall_51(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
1123 let what = if what.first() == Some(&b'>') {
1124 &what[1..]
1125 } else {
1126 what
1127 };
1128 info_tailcall(ar);
1129 let mut status = true;
1130 for &ch in what {
1131 if !matches!(ch, b'S' | b'l' | b'u' | b'n' | b't' | b'r' | b'L' | b'f') {
1132 status = false;
1133 }
1134 }
1135 if what.contains(&b'f') {
1136 state.push(LuaValue::Nil);
1137 }
1138 if what.contains(&b'L') {
1139 state.push(LuaValue::Nil);
1140 }
1141 status
1142}
1143
1144fn info_tailcall(ar: &mut LuaDebug) {
1146 ar.name = Some(Vec::new());
1147 ar.namewhat = Some(b"");
1148 ar.what = Some(b"tail");
1149 ar.linedefined = -1;
1150 ar.lastlinedefined = -1;
1151 ar.currentline = -1;
1152 ar.source = Some(b"=(tail call)".to_vec());
1153 ar.srclen = b"=(tail call)".len();
1154 chunk_id(&mut ar.short_src, b"=(tail call)", b"=(tail call)".len());
1155 ar.nups = 0;
1156 ar.istailcall = false;
1157}
1158
1159#[inline]
1165fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
1166 if pc < jmptarget {
1167 -1
1168 } else {
1169 pc
1170 }
1171}
1172
1173fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
1177 let mut setreg: i32 = -1;
1178 let mut jmptarget: i32 = 0;
1179
1180 let effective_lastpc = if p
1183 .code
1184 .get(lastpc as usize)
1185 .map_or(false, |i| i.is_mm_mode())
1186 {
1187 lastpc - 1
1188 } else {
1189 lastpc
1190 };
1191
1192 for pc in 0..effective_lastpc {
1193 let instr = p.code[pc as usize];
1194 let op = instr.opcode();
1195 let a = instr.arg_a() as i32;
1196
1197 let change = match op {
1198 OpCode::LoadNil => {
1199 let b = instr.arg_b() as i32;
1200 a <= reg && reg <= a + b
1201 }
1202 OpCode::TForCall => reg >= a + 2,
1203 OpCode::Call | OpCode::TailCall => reg >= a,
1204 OpCode::Jmp => {
1205 let b = instr.arg_s_j();
1206 let dest = pc + 1 + b;
1207 if dest <= effective_lastpc && dest > jmptarget {
1208 jmptarget = dest;
1209 }
1210 false
1211 }
1212 _ => {
1213 instr.test_a_mode() && reg == a
1216 }
1217 };
1218
1219 if change {
1220 setreg = filter_pc(pc, jmptarget);
1221 }
1222 }
1223 setreg
1224}
1225
1226fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
1231 match p.k.get(index) {
1234 Some(LuaValue::Str(s)) => {
1235 *name = s.as_bytes();
1237 Some(b"constant")
1238 }
1239 _ => {
1240 *name = b"?";
1241 None
1242 }
1243 }
1244}
1245
1246fn basic_get_obj_name<'a>(
1250 p: &'a LuaProto,
1251 ppc: &mut i32,
1252 reg: i32,
1253 name: &mut &'a [u8],
1254) -> Option<&'static [u8]> {
1255 let pc = *ppc;
1256 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
1258 *name = local_name;
1259 return Some(b"local");
1260 }
1261
1262 *ppc = find_set_reg(p, pc, reg);
1263 let pc = *ppc;
1264
1265 if pc == -1 {
1266 return None;
1267 }
1268
1269 let instr = p.code[pc as usize];
1270 let op = instr.opcode();
1271 match op {
1272 OpCode::Move => {
1273 let b = instr.arg_b() as i32;
1274 if b < instr.arg_a() as i32 {
1275 return basic_get_obj_name(p, ppc, b, name);
1276 }
1277 }
1278 OpCode::GetUpVal => {
1279 *name = upval_name(p, instr.arg_b() as usize);
1280 return Some(b"upvalue");
1281 }
1282 OpCode::LoadK => {
1283 return kname(p, instr.arg_bx() as usize, name);
1284 }
1285 OpCode::LoadKx => {
1286 let next = p.code[(pc + 1) as usize];
1287 return kname(p, next.arg_ax() as usize, name);
1288 }
1289 _ => {}
1290 }
1291 None
1292}
1293
1294fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
1298 let mut pc = pc;
1299 let what = basic_get_obj_name(p, &mut pc, c, name);
1301 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1302 *name = b"?";
1303 }
1304}
1305
1306fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1309 let c = instr.arg_c() as i32;
1310 if instr.arg_k() != 0 {
1312 kname(p, c as usize, name);
1313 } else {
1314 rname(p, pc, c, name);
1315 }
1316}
1317
1318fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1322 let t = instr.arg_b() as usize;
1323 let mut name: &[u8] = b"?";
1324 if isup {
1325 name = upval_name(p, t);
1326 } else {
1327 let mut pc = pc;
1328 let what = basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1329 if !matches!(what, Some(kind) if kind == b"local" || kind == b"upvalue") {
1330 name = b"?";
1331 }
1332 }
1333 if name == LUA_ENV {
1334 b"global"
1335 } else {
1336 b"field"
1337 }
1338}
1339
1340fn get_obj_name<'a>(
1344 p: &'a LuaProto,
1345 lastpc: i32,
1346 reg: i32,
1347 name: &mut &'a [u8],
1348) -> Option<&'static [u8]> {
1349 let mut lastpc = lastpc;
1350 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1351 if kind.is_some() {
1352 return kind;
1353 }
1354
1355 if lastpc == -1 {
1356 return None;
1357 }
1358
1359 let instr = p.code[lastpc as usize];
1360 let op = instr.opcode();
1361 match op {
1362 OpCode::GetTabUp => {
1363 let k = instr.arg_c() as usize;
1364 kname(p, k, name);
1365 Some(is_env(p, lastpc, instr, true))
1366 }
1367 OpCode::GetTable => {
1368 let k = instr.arg_c() as i32;
1369 rname(p, lastpc, k, name);
1370 Some(is_env(p, lastpc, instr, false))
1371 }
1372 OpCode::GetI => {
1373 *name = b"integer index";
1374 Some(b"field")
1375 }
1376 OpCode::GetField => {
1377 let k = instr.arg_c() as usize;
1378 kname(p, k, name);
1379 Some(is_env(p, lastpc, instr, false))
1380 }
1381 OpCode::Self_ => {
1382 rkname(p, lastpc, instr, name);
1383 Some(b"method")
1384 }
1385 _ => None,
1386 }
1387}
1388
1389fn funcname_from_code<'a>(
1396 state: &LuaState,
1397 p: &'a LuaProto,
1398 pc: i32,
1399 name: &mut Option<Vec<u8>>,
1400) -> Option<&'static [u8]> {
1401 let instr = p.code[pc as usize];
1402 let op = instr.opcode();
1403
1404 match op {
1405 OpCode::Call | OpCode::TailCall => {
1406 let mut name_bytes: &[u8] = b"?";
1407 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1408 *name = Some(name_bytes.to_vec());
1409 kind
1410 }
1411 OpCode::TForCall => {
1412 *name = Some(b"for iterator".to_vec());
1413 Some(b"for iterator")
1414 }
1415 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1417 get_tm_name(state, TagMethod::Index, name)
1418 }
1419 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1420 get_tm_name(state, TagMethod::NewIndex, name)
1421 }
1422 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1423 let tm_idx = instr.arg_c() as u8;
1426 let tm = TagMethod::from_u8(tm_idx);
1427 get_tm_name(state, tm, name)
1428 }
1429 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1430 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1431 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1432 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1433 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1434 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1435 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1436 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1437 _ => None,
1438 }
1439}
1440
1441fn get_tm_name(
1451 state: &LuaState,
1452 tm: TagMethod,
1453 name: &mut Option<Vec<u8>>,
1454) -> Option<&'static [u8]> {
1455 if state.global().lua_version == lua_types::LuaVersion::V51 {
1456 return None;
1457 }
1458 let raw_bytes: Vec<u8> = state
1462 .global()
1463 .tm_name(tm)
1464 .map(|s| s.as_bytes().to_vec())
1465 .unwrap_or_default();
1466 let keeps_prefix = matches!(
1467 state.global().lua_version,
1468 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1469 );
1470 let resolved = if keeps_prefix {
1471 raw_bytes
1472 } else {
1473 raw_bytes.strip_prefix(b"__").unwrap_or(&raw_bytes).to_vec()
1474 };
1475 *name = Some(resolved);
1476 Some(b"metamethod")
1477}
1478
1479fn funcname_from_call<'a>(
1482 state: &'a LuaState,
1483 ci: &CallInfo,
1484 name: &mut Option<Vec<u8>>,
1485) -> Option<&'static [u8]> {
1486 if ci.callstatus & CIST_HOOKED != 0 {
1487 *name = Some(b"?".to_vec());
1488 return Some(b"hook");
1489 }
1490 if ci.callstatus & CIST_FIN != 0 {
1491 *name = Some(b"__gc".to_vec());
1492 return Some(b"metamethod");
1493 }
1494 if ci.is_lua() {
1495 let proto = ci_lua_proto(ci, state);
1496 return funcname_from_code(state, &proto, current_pc(ci), name);
1497 }
1498 None
1499}
1500
1501fn in_stack(ci: &CallInfo, val_idx: StackIdx) -> i32 {
1512 let base = StackIdx(ci.func.0 + 1);
1513 let ci_top = ci.top;
1516 let mut pos = 0i32;
1517 let mut cur = base;
1518 while cur.0 < ci_top.0 {
1519 if cur == val_idx {
1520 return pos;
1521 }
1522 cur = StackIdx(cur.0 + 1);
1523 pos += 1;
1524 }
1525 -1
1526}
1527
1528fn get_upval_name<'a>(
1537 ci: &CallInfo,
1538 val_idx: StackIdx,
1539 name: &mut &'a [u8],
1540 state: &'a LuaState,
1541) -> Option<&'static [u8]> {
1542 let proto = ci_lua_proto(ci, state);
1543 let lua_cl = match state.get_at(ci.func) {
1546 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1547 _ => return None,
1548 };
1549 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1550 let upval = upval_slot.get();
1551 if let Some((_thread_id, idx)) = upval.try_open_payload() {
1552 if idx == val_idx {
1553 let _ = upval_name(&proto, i);
1556 *name = b"upvalue";
1557 return Some(b"upvalue");
1558 }
1559 }
1560 }
1561 None
1562}
1563
1564fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1569 match (kind, name) {
1570 (Some(k), Some(n)) => {
1571 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1572 out.extend_from_slice(b" (");
1573 out.extend_from_slice(k);
1574 out.extend_from_slice(b" '");
1575 out.extend_from_slice(n);
1576 out.extend_from_slice(b"')");
1577 out
1578 }
1579 _ => Vec::new(),
1580 }
1581}
1582
1583fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1587 let (kind, name) = var_info_parts(state, val_idx);
1588 format_var_info(kind.as_deref(), name.as_deref())
1589}
1590
1591fn var_info_parts(state: &LuaState, val_idx: StackIdx) -> (Option<Vec<u8>>, Option<Vec<u8>>) {
1598 let ci_idx = state.current_ci_idx();
1599 let ci = state.get_ci(ci_idx).clone();
1600 let mut kind: Option<&[u8]> = None;
1601 let mut name_owned: Vec<u8> = b"?".to_vec();
1602
1603 if ci.is_lua() {
1604 let mut up_name: &[u8] = b"?";
1605 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1606 if kind.is_some() {
1607 name_owned = up_name.to_vec();
1608 } else {
1609 let reg = in_stack(&ci, val_idx);
1610 if reg >= 0 {
1611 let proto = ci_lua_proto(&ci, state);
1612 let mut nref: &[u8] = b"?";
1613 let pc = current_pc(&ci);
1614 let k = get_obj_name(&proto, pc, reg, &mut nref);
1615 kind = k;
1616 if kind.is_some() {
1617 name_owned = nref.to_vec();
1618 }
1619 }
1620 }
1621 }
1622 match kind {
1623 Some(k) => (Some(k.to_vec()), Some(name_owned)),
1624 None => (None, None),
1625 }
1626}
1627
1628fn typeerror_inner_parts(
1639 state: &LuaState,
1640 val: &LuaValue,
1641 op: &[u8],
1642 kind: Option<&[u8]>,
1643 name: Option<&[u8]>,
1644) -> LuaError {
1645 let t = state.obj_type_name(val);
1646 let legacy_order = matches!(
1647 state.global().lua_version,
1648 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
1649 );
1650 let mut msg = Vec::new();
1651 msg.extend_from_slice(b"attempt to ");
1652 msg.extend_from_slice(op);
1653 if let (true, Some(k), Some(n)) = (legacy_order, kind, name) {
1654 msg.extend_from_slice(b" ");
1655 msg.extend_from_slice(k);
1656 msg.extend_from_slice(b" '");
1657 msg.extend_from_slice(n);
1658 msg.extend_from_slice(b"' (a ");
1659 msg.extend_from_slice(&t);
1660 msg.extend_from_slice(b" value)");
1661 } else {
1662 msg.extend_from_slice(b" a ");
1663 msg.extend_from_slice(&t);
1664 msg.extend_from_slice(b" value");
1665 msg.extend_from_slice(&format_var_info(kind, name));
1666 }
1667 prefixed_runtime(state, msg)
1668}
1669
1670pub(crate) fn type_error(
1674 state: &LuaState,
1675 val: &LuaValue,
1676 val_idx: StackIdx,
1677 op: &[u8],
1678) -> LuaError {
1679 let (kind, name) = var_info_parts(state, val_idx);
1680 typeerror_inner_parts(state, val, op, kind.as_deref(), name.as_deref())
1681}
1682
1683pub(crate) fn arith_type_error(
1698 state: &LuaState,
1699 val: &LuaValue,
1700 val_idx: StackIdx,
1701 op: &[u8],
1702 binary: bool,
1703) -> LuaError {
1704 let (kind, name) = var_info_parts(state, val_idx);
1705 let is_constant = matches!(kind.as_deref(), Some(b"constant"));
1706 let suppress_constant = is_constant
1707 && match state.global().lua_version {
1708 lua_types::LuaVersion::V51 => true,
1709 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 => binary,
1710 _ => false,
1711 };
1712 let (kind, name) = if suppress_constant {
1713 (None, None)
1714 } else {
1715 (kind, name)
1716 };
1717 typeerror_inner_parts(state, val, op, kind.as_deref(), name.as_deref())
1718}
1719
1720pub(crate) fn type_error_with_hint(
1726 state: &LuaState,
1727 val: &LuaValue,
1728 op: &[u8],
1729 kind: &[u8],
1730 name: &[u8],
1731) -> LuaError {
1732 let t = obj_type_name_static(val);
1733 let legacy_order = matches!(
1734 state.global().lua_version,
1735 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
1736 );
1737 let mut msg = Vec::new();
1738 msg.extend_from_slice(b"attempt to ");
1739 msg.extend_from_slice(op);
1740 if legacy_order {
1741 msg.extend_from_slice(b" ");
1742 msg.extend_from_slice(kind);
1743 msg.extend_from_slice(b" '");
1744 msg.extend_from_slice(name);
1745 msg.extend_from_slice(b"' (a ");
1746 msg.extend_from_slice(t);
1747 msg.extend_from_slice(b" value)");
1748 } else {
1749 msg.extend_from_slice(b" a ");
1750 msg.extend_from_slice(t);
1751 msg.extend_from_slice(b" value");
1752 msg.extend_from_slice(&format_var_info(Some(kind), Some(name)));
1753 }
1754 prefixed_runtime(state, msg)
1755}
1756
1757fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1760 match val {
1761 LuaValue::Nil => b"nil",
1762 LuaValue::Bool(_) => b"boolean",
1763 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1764 LuaValue::Str(_) => b"string",
1765 LuaValue::Table(_) => b"table",
1766 LuaValue::Function(_) => b"function",
1767 LuaValue::UserData(_) => b"userdata",
1768 LuaValue::LightUserData(_) => b"light userdata",
1769 LuaValue::Thread(_) => b"thread",
1770 }
1771}
1772
1773pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1782 let uses_callerror = matches!(
1783 state.global().lua_version,
1784 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
1785 );
1786 let (kind, name) = if uses_callerror {
1787 let ci_idx = state.current_ci_idx();
1788 let ci = state.get_ci(ci_idx).clone();
1789 let mut name: Option<Vec<u8>> = None;
1790 let kind = funcname_from_call(state, &ci, &mut name);
1791 if kind.is_some() {
1792 (kind.map(|k| k.to_vec()), name)
1793 } else {
1794 var_info_parts(state, val_idx)
1795 }
1796 } else {
1797 var_info_parts(state, val_idx)
1798 };
1799 typeerror_inner_parts(state, val, b"call", kind.as_deref(), name.as_deref())
1800}
1801
1802pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1805 if matches!(
1809 state.global().lua_version,
1810 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1811 ) {
1812 let mut msg = Vec::new();
1813 msg.extend_from_slice(b"'for' ");
1814 msg.extend_from_slice(what);
1815 msg.extend_from_slice(b" must be a number");
1816 return prefixed_runtime(state, msg);
1817 }
1818 let t = crate::tagmethods::obj_type_name(state, val)
1819 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1820 let mut msg = Vec::new();
1821 msg.extend_from_slice(b"bad 'for' ");
1822 msg.extend_from_slice(what);
1823 msg.extend_from_slice(b" (number expected, got ");
1824 msg.extend_from_slice(&t);
1825 msg.push(b')');
1826 prefixed_runtime(state, msg)
1827}
1828
1829pub(crate) fn op_int_error(
1833 state: &LuaState,
1834 p1: &LuaValue,
1835 p1_idx: StackIdx,
1836 p2: &LuaValue,
1837 p2_idx: StackIdx,
1838 msg: &[u8],
1839) -> LuaError {
1840 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1842 (p1, p1_idx)
1843 } else {
1844 (p2, p2_idx)
1845 };
1846 type_error(state, bad_val, bad_idx, msg)
1847}
1848
1849pub(crate) fn to_int_error(
1855 state: &LuaState,
1856 p1: &LuaValue,
1857 p1_idx: Option<StackIdx>,
1858 _p2: &LuaValue,
1859 p2_idx: Option<StackIdx>,
1860) -> LuaError {
1861 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1862 p1_idx
1863 } else {
1864 p2_idx
1865 };
1866 let extra = match bad_idx {
1867 Some(idx) => var_info(state, idx),
1868 None => Vec::new(),
1869 };
1870 let mut msg = Vec::new();
1871 msg.extend_from_slice(b"number");
1872 msg.extend_from_slice(&extra);
1873 msg.extend_from_slice(b" has no integer representation");
1874 prefixed_runtime(state, msg)
1875}
1876
1877pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1880 let t1 = state.obj_type_name(p1);
1882 let t2 = state.obj_type_name(p2);
1883 let msg = if t1 == t2 {
1885 let mut m = Vec::new();
1886 m.extend_from_slice(b"attempt to compare two ");
1887 m.extend_from_slice(&t1);
1888 m.extend_from_slice(b" values");
1889 m
1890 } else {
1891 let mut m = Vec::new();
1892 m.extend_from_slice(b"attempt to compare ");
1893 m.extend_from_slice(&t1);
1894 m.extend_from_slice(b" with ");
1895 m.extend_from_slice(&t2);
1896 m
1897 };
1898 prefixed_runtime(state, msg)
1899}
1900
1901pub(crate) fn add_info(
1910 _state: Option<&mut LuaState>,
1911 msg: &[u8],
1912 src: Option<&LuaString>,
1913 line: i32,
1914 unknown_line_as_question: bool,
1915) -> Vec<u8> {
1916 let mut buff = [0u8; LUA_IDSIZE];
1918 if let Some(src) = src {
1919 chunk_id(&mut buff, src.as_bytes(), src.len());
1922 } else if unknown_line_as_question {
1923 let mut out = Vec::with_capacity(5 + msg.len());
1924 out.extend_from_slice(b"?:?: ");
1925 out.extend_from_slice(msg);
1926 return out;
1927 } else {
1928 buff[0] = b'?';
1929 }
1930 let src_part = buff
1933 .iter()
1934 .position(|&b| b == 0)
1935 .map_or(&buff[..], |n| &buff[..n]);
1936 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1937 out.extend_from_slice(src_part);
1938 out.push(b':');
1939 let line_str = line.to_string();
1941 out.extend_from_slice(line_str.as_bytes());
1942 out.extend_from_slice(b": ");
1943 out.extend_from_slice(msg);
1944 out
1945}
1946
1947fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1952 if p.lineinfo.is_empty() {
1953 return false;
1954 }
1955
1956 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1957 let mut delta: i32 = 0;
1958 let mut pc = oldpc;
1959 loop {
1960 pc += 1;
1961 if pc as usize >= p.lineinfo.len() {
1962 break;
1963 }
1964 let lineinfo = p.lineinfo[pc as usize];
1965 if lineinfo == ABS_LINE_INFO {
1966 break;
1967 }
1968 delta += lineinfo as i32;
1969 if pc == newpc {
1970 return delta != 0;
1971 }
1972 }
1973 }
1974 get_func_line(p, oldpc) != get_func_line(p, newpc)
1975}
1976
1977pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1983 let ci_idx = state.current_ci_idx();
1984 let ci = state.get_ci(ci_idx).clone();
1985 state.get_ci_mut(ci_idx).set_trap(true);
1986 let proto = ci_lua_proto(&ci, state);
1987
1988 if ci.saved_pc() == 0 {
1989 if proto.is_vararg {
1990 return Ok(0);
1991 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1992 state.hook_call(ci_idx)?;
1994 }
1995 }
1996 Ok(1)
1997}
1998
1999pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
2009 let ci_idx = state.current_ci_idx();
2010 let ci = state.get_ci(ci_idx).clone();
2011
2012 let mask = state.hook_mask();
2013
2014 if !state.allowhook {
2015 return Ok(1);
2016 }
2017
2018 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
2019 state.get_ci_mut(ci_idx).set_trap(false);
2020 return Ok(0);
2021 }
2022
2023 let next_pc = pc + 1;
2024 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
2025
2026 let counthook = if mask & LUA_MASKCOUNT != 0 {
2027 let hc = state.hook_count() - 1;
2028 state.set_hook_count(hc);
2029 hc == 0
2030 } else {
2031 false
2032 };
2033
2034 if counthook {
2035 state.reset_hook_count();
2036 } else if mask & LUA_MASKLINE == 0 {
2037 return Ok(1);
2038 }
2039
2040 if counthook {
2045 if let Some(err) = state.sandbox_charge_interval() {
2046 return Err(err);
2047 }
2048 }
2049
2050 if ci.callstatus & CIST_HOOKYIELD != 0 {
2051 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
2052 return Ok(1);
2053 }
2054
2055 if state.ci_lua_closure(ci_idx).is_none() {
2056 return Ok(1);
2057 }
2058
2059 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
2062 if !cur_instr.is_in_top() {
2063 let ci_top = state.get_ci(ci_idx).top;
2064 state.set_top(ci_top);
2065 }
2066
2067 if counthook {
2068 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
2070 }
2071
2072 if mask & LUA_MASKLINE != 0 {
2073 let proto = ci_lua_proto(&ci, state);
2074 let oldpc = if state.old_pc() < proto.code.len() as u32 {
2075 state.old_pc() as i32
2076 } else {
2077 0
2078 };
2079 let npci = next_pc as i32 - 1;
2081
2082 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
2083 let newline = get_func_line(&proto, npci);
2084 state.call_hook_event(LUA_HOOKLINE, newline)?;
2086 }
2087 state.set_old_pc(npci as u32);
2088 }
2089
2090 if state.status() == lua_types::status::LuaStatus::Yield {
2091 if counthook {
2092 state.set_hook_count(1);
2093 }
2094 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
2095 return Err(LuaError::Yield);
2097 }
2098
2099 Ok(1)
2100}
2101
2102fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
2110 out.fill(0);
2111 let n = crate::object::chunk_id(&mut out[..], source);
2112 if n < out.len() {
2113 out[n] = 0;
2114 }
2115}
2116
2117fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
2121 crate::func::get_local_name(p, n, pc)
2122}
2123
2124fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
2126 get_local_name(&cl.proto, n, pc)
2127}
2128
2129fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
2143 match state.get_at(ci.func) {
2144 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
2145 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
2146 }
2147}
2148
2149