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>(
903 state: &'a LuaState,
904 ci: Option<&CallInfo>,
905 name: &mut Option<Vec<u8>>,
906) -> Option<&'static [u8]> {
907 let ci = ci?;
910 if ci.callstatus & CIST_TAIL != 0 {
911 return None;
912 }
913 let prev_idx = ci.previous?;
916 let prev_ci = state.get_ci(prev_idx).clone();
917 funcname_from_call(state, &prev_ci, name)
918}
919
920fn aux_get_info(
923 state: &LuaState,
924 what: &[u8],
925 ar: &mut LuaDebug,
926 cl: Option<&LuaClosure>,
927 ci: Option<&CallInfo>,
928) -> bool {
929 let mut status = true;
930 for &ch in what {
931 match ch {
932 b'S' => {
933 func_info(ar, cl);
934 }
935 b'l' => {
936 ar.currentline = match ci {
937 Some(ci) if ci.is_lua() => get_current_line(ci, state),
938 _ => -1,
939 };
940 }
941 b'u' => {
942 ar.nups = cl.map_or(0, |c| c.nupvalues() as u8);
943 match cl {
944 Some(LuaClosure::Lua(lua_cl)) => {
945 ar.isvararg = lua_cl.proto.is_vararg;
947 ar.nparams = lua_cl.proto.numparams;
948 if state.global().lua_version == lua_types::LuaVersion::V51 {
949 ar.nups = visible_upvalue_count_51(&lua_cl.proto) as u8;
950 }
951 }
952 _ => {
953 ar.isvararg = true;
954 ar.nparams = 0;
955 }
956 }
957 }
958 b't' => {
959 if let Some(ci) = ci {
960 ar.istailcall = ci.callstatus & CIST_TAIL != 0;
961 ar.extraargs = ci.call_metamethods;
962 } else {
963 ar.istailcall = false;
964 ar.extraargs = 0;
965 }
966 }
967 b'n' => {
968 let mut name: Option<Vec<u8>> = None;
969 ar.namewhat = get_func_name(state, ci, &mut name);
970 if ar.namewhat.is_none() {
971 ar.namewhat = Some(b"");
972 ar.name = None;
973 } else {
974 ar.name = name;
975 }
976 }
977 b'r' => match ci {
979 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
980 ar.ftransfer = ci.transfer_ftransfer();
982 ar.ntransfer = ci.transfer_ntransfer();
983 }
984 _ => {
985 ar.ftransfer = 0;
986 ar.ntransfer = 0;
987 }
988 },
989 b'L' | b'f' => {}
990 _ => {
991 status = false;
992 }
993 }
994 }
995 status
996}
997
998pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
1001 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
1002 let func_val = state.peek_at(state.top_idx() - 1).clone();
1003 state.pop_n(1);
1004 debug_assert!(
1005 matches!(func_val, LuaValue::Function(_)),
1006 "get_info: function expected"
1007 );
1008 let cl = match &func_val {
1009 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
1010 LuaValue::Function(c) => c.clone(),
1011 _ => unreachable!(),
1012 }),
1013 _ => None,
1014 };
1015 (cl, None, func_val, &what[1..])
1016 } else {
1017 let ci_idx = match ar.i_ci {
1018 Some(i) => i,
1019 None => return false,
1020 };
1021 if state.global().lua_version == lua_types::LuaVersion::V51
1022 && state.is_base_ci(ci_idx)
1023 {
1024 return get_info_tailcall_51(state, what, ar);
1025 }
1026 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
1027 debug_assert!(
1028 matches!(func_val, LuaValue::Function(_)),
1029 "get_info: non-function at ci->func"
1030 );
1031 let cl = match &func_val {
1032 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => Some(match &func_val {
1033 LuaValue::Function(c) => c.clone(),
1034 _ => unreachable!(),
1035 }),
1036 _ => None,
1037 };
1038 (cl, Some(ci_idx), func_val, what)
1039 };
1040
1041 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
1042 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
1043
1044 if what.contains(&b'f') {
1045 state.push(func_val);
1046 }
1047 if what.contains(&b'L') {
1048 let _ = collect_valid_lines(state, cl.as_ref());
1050 }
1051 status
1052}
1053
1054fn get_info_tailcall_51(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
1073 let what = if what.first() == Some(&b'>') {
1074 &what[1..]
1075 } else {
1076 what
1077 };
1078 info_tailcall(ar);
1079 let mut status = true;
1080 for &ch in what {
1081 if !matches!(ch, b'S' | b'l' | b'u' | b'n' | b't' | b'r' | b'L' | b'f') {
1082 status = false;
1083 }
1084 }
1085 if what.contains(&b'f') {
1086 state.push(LuaValue::Nil);
1087 }
1088 if what.contains(&b'L') {
1089 state.push(LuaValue::Nil);
1090 }
1091 status
1092}
1093
1094fn info_tailcall(ar: &mut LuaDebug) {
1096 ar.name = Some(Vec::new());
1097 ar.namewhat = Some(b"");
1098 ar.what = Some(b"tail");
1099 ar.linedefined = -1;
1100 ar.lastlinedefined = -1;
1101 ar.currentline = -1;
1102 ar.source = Some(b"=(tail call)".to_vec());
1103 ar.srclen = b"=(tail call)".len();
1104 chunk_id(&mut ar.short_src, b"=(tail call)", b"=(tail call)".len());
1105 ar.nups = 0;
1106 ar.istailcall = false;
1107}
1108
1109#[inline]
1115fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
1116 if pc < jmptarget {
1117 -1
1118 } else {
1119 pc
1120 }
1121}
1122
1123fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
1127 let mut setreg: i32 = -1;
1128 let mut jmptarget: i32 = 0;
1129
1130 let effective_lastpc = if p
1133 .code
1134 .get(lastpc as usize)
1135 .map_or(false, |i| i.is_mm_mode())
1136 {
1137 lastpc - 1
1138 } else {
1139 lastpc
1140 };
1141
1142 for pc in 0..effective_lastpc {
1143 let instr = p.code[pc as usize];
1144 let op = instr.opcode();
1145 let a = instr.arg_a() as i32;
1146
1147 let change = match op {
1148 OpCode::LoadNil => {
1149 let b = instr.arg_b() as i32;
1150 a <= reg && reg <= a + b
1151 }
1152 OpCode::TForCall => reg >= a + 2,
1153 OpCode::Call | OpCode::TailCall => reg >= a,
1154 OpCode::Jmp => {
1155 let b = instr.arg_s_j();
1156 let dest = pc + 1 + b;
1157 if dest <= effective_lastpc && dest > jmptarget {
1158 jmptarget = dest;
1159 }
1160 false
1161 }
1162 _ => {
1163 instr.test_a_mode() && reg == a
1166 }
1167 };
1168
1169 if change {
1170 setreg = filter_pc(pc, jmptarget);
1171 }
1172 }
1173 setreg
1174}
1175
1176fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
1181 match p.k.get(index) {
1184 Some(LuaValue::Str(s)) => {
1185 *name = s.as_bytes();
1187 Some(b"constant")
1188 }
1189 _ => {
1190 *name = b"?";
1191 None
1192 }
1193 }
1194}
1195
1196fn basic_get_obj_name<'a>(
1200 p: &'a LuaProto,
1201 ppc: &mut i32,
1202 reg: i32,
1203 name: &mut &'a [u8],
1204) -> Option<&'static [u8]> {
1205 let pc = *ppc;
1206 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
1208 *name = local_name;
1209 return Some(b"local");
1210 }
1211
1212 *ppc = find_set_reg(p, pc, reg);
1213 let pc = *ppc;
1214
1215 if pc == -1 {
1216 return None;
1217 }
1218
1219 let instr = p.code[pc as usize];
1220 let op = instr.opcode();
1221 match op {
1222 OpCode::Move => {
1223 let b = instr.arg_b() as i32;
1224 if b < instr.arg_a() as i32 {
1225 return basic_get_obj_name(p, ppc, b, name);
1226 }
1227 }
1228 OpCode::GetUpVal => {
1229 *name = upval_name(p, instr.arg_b() as usize);
1230 return Some(b"upvalue");
1231 }
1232 OpCode::LoadK => {
1233 return kname(p, instr.arg_bx() as usize, name);
1234 }
1235 OpCode::LoadKx => {
1236 let next = p.code[(pc + 1) as usize];
1237 return kname(p, next.arg_ax() as usize, name);
1238 }
1239 _ => {}
1240 }
1241 None
1242}
1243
1244fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
1248 let mut pc = pc;
1249 let what = basic_get_obj_name(p, &mut pc, c, name);
1251 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1252 *name = b"?";
1253 }
1254}
1255
1256fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1259 let c = instr.arg_c() as i32;
1260 if instr.arg_k() != 0 {
1262 kname(p, c as usize, name);
1263 } else {
1264 rname(p, pc, c, name);
1265 }
1266}
1267
1268fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1272 let t = instr.arg_b() as usize;
1273 let mut name: &[u8] = b"?";
1274 if isup {
1275 name = upval_name(p, t);
1276 } else {
1277 let mut pc = pc;
1278 let what = basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1279 if !matches!(what, Some(kind) if kind == b"local" || kind == b"upvalue") {
1280 name = b"?";
1281 }
1282 }
1283 if name == LUA_ENV {
1284 b"global"
1285 } else {
1286 b"field"
1287 }
1288}
1289
1290fn get_obj_name<'a>(
1294 p: &'a LuaProto,
1295 lastpc: i32,
1296 reg: i32,
1297 name: &mut &'a [u8],
1298) -> Option<&'static [u8]> {
1299 let mut lastpc = lastpc;
1300 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1301 if kind.is_some() {
1302 return kind;
1303 }
1304
1305 if lastpc == -1 {
1306 return None;
1307 }
1308
1309 let instr = p.code[lastpc as usize];
1310 let op = instr.opcode();
1311 match op {
1312 OpCode::GetTabUp => {
1313 let k = instr.arg_c() as usize;
1314 kname(p, k, name);
1315 Some(is_env(p, lastpc, instr, true))
1316 }
1317 OpCode::GetTable => {
1318 let k = instr.arg_c() as i32;
1319 rname(p, lastpc, k, name);
1320 Some(is_env(p, lastpc, instr, false))
1321 }
1322 OpCode::GetI => {
1323 *name = b"integer index";
1324 Some(b"field")
1325 }
1326 OpCode::GetField => {
1327 let k = instr.arg_c() as usize;
1328 kname(p, k, name);
1329 Some(is_env(p, lastpc, instr, false))
1330 }
1331 OpCode::Self_ => {
1332 rkname(p, lastpc, instr, name);
1333 Some(b"method")
1334 }
1335 _ => None,
1336 }
1337}
1338
1339fn funcname_from_code<'a>(
1346 state: &LuaState,
1347 p: &'a LuaProto,
1348 pc: i32,
1349 name: &mut Option<Vec<u8>>,
1350) -> Option<&'static [u8]> {
1351 let instr = p.code[pc as usize];
1352 let op = instr.opcode();
1353
1354 match op {
1355 OpCode::Call | OpCode::TailCall => {
1356 let mut name_bytes: &[u8] = b"?";
1357 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1358 *name = Some(name_bytes.to_vec());
1359 kind
1360 }
1361 OpCode::TForCall => {
1362 *name = Some(b"for iterator".to_vec());
1363 Some(b"for iterator")
1364 }
1365 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1367 get_tm_name(state, TagMethod::Index, name)
1368 }
1369 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1370 get_tm_name(state, TagMethod::NewIndex, name)
1371 }
1372 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1373 let tm_idx = instr.arg_c() as u8;
1376 let tm = TagMethod::from_u8(tm_idx);
1377 get_tm_name(state, tm, name)
1378 }
1379 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1380 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1381 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1382 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1383 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1384 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1385 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1386 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1387 _ => None,
1388 }
1389}
1390
1391fn get_tm_name(
1401 state: &LuaState,
1402 tm: TagMethod,
1403 name: &mut Option<Vec<u8>>,
1404) -> Option<&'static [u8]> {
1405 if state.global().lua_version == lua_types::LuaVersion::V51 {
1406 return None;
1407 }
1408 let raw_bytes: Vec<u8> = state
1412 .global()
1413 .tm_name(tm)
1414 .map(|s| s.as_bytes().to_vec())
1415 .unwrap_or_default();
1416 let keeps_prefix = matches!(
1417 state.global().lua_version,
1418 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1419 );
1420 let resolved = if keeps_prefix {
1421 raw_bytes
1422 } else {
1423 raw_bytes.strip_prefix(b"__").unwrap_or(&raw_bytes).to_vec()
1424 };
1425 *name = Some(resolved);
1426 Some(b"metamethod")
1427}
1428
1429fn funcname_from_call<'a>(
1432 state: &'a LuaState,
1433 ci: &CallInfo,
1434 name: &mut Option<Vec<u8>>,
1435) -> Option<&'static [u8]> {
1436 if ci.callstatus & CIST_HOOKED != 0 {
1437 *name = Some(b"?".to_vec());
1438 return Some(b"hook");
1439 }
1440 if ci.callstatus & CIST_FIN != 0 {
1441 *name = Some(b"__gc".to_vec());
1442 return Some(b"metamethod");
1443 }
1444 if ci.is_lua() {
1445 let proto = ci_lua_proto(ci, state);
1446 return funcname_from_code(state, &proto, current_pc(ci), name);
1447 }
1448 None
1449}
1450
1451fn in_stack(ci: &CallInfo, val_idx: StackIdx) -> i32 {
1462 let base = StackIdx(ci.func.0 + 1);
1463 let ci_top = ci.top;
1466 let mut pos = 0i32;
1467 let mut cur = base;
1468 while cur.0 < ci_top.0 {
1469 if cur == val_idx {
1470 return pos;
1471 }
1472 cur = StackIdx(cur.0 + 1);
1473 pos += 1;
1474 }
1475 -1
1476}
1477
1478fn get_upval_name<'a>(
1487 ci: &CallInfo,
1488 val_idx: StackIdx,
1489 name: &mut &'a [u8],
1490 state: &'a LuaState,
1491) -> Option<&'static [u8]> {
1492 let proto = ci_lua_proto(ci, state);
1493 let lua_cl = match state.get_at(ci.func) {
1496 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1497 _ => return None,
1498 };
1499 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1500 let upval = upval_slot.get();
1501 if let Some((_thread_id, idx)) = upval.try_open_payload() {
1502 if idx == val_idx {
1503 let _ = upval_name(&proto, i);
1506 *name = b"upvalue";
1507 return Some(b"upvalue");
1508 }
1509 }
1510 }
1511 None
1512}
1513
1514fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1519 match (kind, name) {
1520 (Some(k), Some(n)) => {
1521 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1522 out.extend_from_slice(b" (");
1523 out.extend_from_slice(k);
1524 out.extend_from_slice(b" '");
1525 out.extend_from_slice(n);
1526 out.extend_from_slice(b"')");
1527 out
1528 }
1529 _ => Vec::new(),
1530 }
1531}
1532
1533fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1537 let (kind, name) = var_info_parts(state, val_idx);
1538 format_var_info(kind.as_deref(), name.as_deref())
1539}
1540
1541fn var_info_parts(state: &LuaState, val_idx: StackIdx) -> (Option<Vec<u8>>, Option<Vec<u8>>) {
1548 let ci_idx = state.current_ci_idx();
1549 let ci = state.get_ci(ci_idx).clone();
1550 let mut kind: Option<&[u8]> = None;
1551 let mut name_owned: Vec<u8> = b"?".to_vec();
1552
1553 if ci.is_lua() {
1554 let mut up_name: &[u8] = b"?";
1555 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1556 if kind.is_some() {
1557 name_owned = up_name.to_vec();
1558 } else {
1559 let reg = in_stack(&ci, val_idx);
1560 if reg >= 0 {
1561 let proto = ci_lua_proto(&ci, state);
1562 let mut nref: &[u8] = b"?";
1563 let pc = current_pc(&ci);
1564 let k = get_obj_name(&proto, pc, reg, &mut nref);
1565 kind = k;
1566 if kind.is_some() {
1567 name_owned = nref.to_vec();
1568 }
1569 }
1570 }
1571 }
1572 match kind {
1573 Some(k) => (Some(k.to_vec()), Some(name_owned)),
1574 None => (None, None),
1575 }
1576}
1577
1578fn typeerror_inner_parts(
1589 state: &LuaState,
1590 val: &LuaValue,
1591 op: &[u8],
1592 kind: Option<&[u8]>,
1593 name: Option<&[u8]>,
1594) -> LuaError {
1595 let t = state.obj_type_name(val);
1596 let legacy_order = matches!(
1597 state.global().lua_version,
1598 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
1599 );
1600 let mut msg = Vec::new();
1601 msg.extend_from_slice(b"attempt to ");
1602 msg.extend_from_slice(op);
1603 if let (true, Some(k), Some(n)) = (legacy_order, kind, name) {
1604 msg.extend_from_slice(b" ");
1605 msg.extend_from_slice(k);
1606 msg.extend_from_slice(b" '");
1607 msg.extend_from_slice(n);
1608 msg.extend_from_slice(b"' (a ");
1609 msg.extend_from_slice(&t);
1610 msg.extend_from_slice(b" value)");
1611 } else {
1612 msg.extend_from_slice(b" a ");
1613 msg.extend_from_slice(&t);
1614 msg.extend_from_slice(b" value");
1615 msg.extend_from_slice(&format_var_info(kind, name));
1616 }
1617 prefixed_runtime(state, msg)
1618}
1619
1620pub(crate) fn type_error(
1624 state: &LuaState,
1625 val: &LuaValue,
1626 val_idx: StackIdx,
1627 op: &[u8],
1628) -> LuaError {
1629 let (kind, name) = var_info_parts(state, val_idx);
1630 typeerror_inner_parts(state, val, op, kind.as_deref(), name.as_deref())
1631}
1632
1633pub(crate) fn arith_type_error(
1648 state: &LuaState,
1649 val: &LuaValue,
1650 val_idx: StackIdx,
1651 op: &[u8],
1652 binary: bool,
1653) -> LuaError {
1654 let (kind, name) = var_info_parts(state, val_idx);
1655 let is_constant = matches!(kind.as_deref(), Some(b"constant"));
1656 let suppress_constant = is_constant
1657 && match state.global().lua_version {
1658 lua_types::LuaVersion::V51 => true,
1659 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 => binary,
1660 _ => false,
1661 };
1662 let (kind, name) = if suppress_constant {
1663 (None, None)
1664 } else {
1665 (kind, name)
1666 };
1667 typeerror_inner_parts(state, val, op, kind.as_deref(), name.as_deref())
1668}
1669
1670pub(crate) fn type_error_with_hint(
1676 state: &LuaState,
1677 val: &LuaValue,
1678 op: &[u8],
1679 kind: &[u8],
1680 name: &[u8],
1681) -> LuaError {
1682 let t = obj_type_name_static(val);
1683 let legacy_order = matches!(
1684 state.global().lua_version,
1685 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
1686 );
1687 let mut msg = Vec::new();
1688 msg.extend_from_slice(b"attempt to ");
1689 msg.extend_from_slice(op);
1690 if legacy_order {
1691 msg.extend_from_slice(b" ");
1692 msg.extend_from_slice(kind);
1693 msg.extend_from_slice(b" '");
1694 msg.extend_from_slice(name);
1695 msg.extend_from_slice(b"' (a ");
1696 msg.extend_from_slice(t);
1697 msg.extend_from_slice(b" value)");
1698 } else {
1699 msg.extend_from_slice(b" a ");
1700 msg.extend_from_slice(t);
1701 msg.extend_from_slice(b" value");
1702 msg.extend_from_slice(&format_var_info(Some(kind), Some(name)));
1703 }
1704 prefixed_runtime(state, msg)
1705}
1706
1707fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1710 match val {
1711 LuaValue::Nil => b"nil",
1712 LuaValue::Bool(_) => b"boolean",
1713 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1714 LuaValue::Str(_) => b"string",
1715 LuaValue::Table(_) => b"table",
1716 LuaValue::Function(_) => b"function",
1717 LuaValue::UserData(_) => b"userdata",
1718 LuaValue::LightUserData(_) => b"light userdata",
1719 LuaValue::Thread(_) => b"thread",
1720 }
1721}
1722
1723pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1732 let uses_callerror = matches!(
1733 state.global().lua_version,
1734 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
1735 );
1736 let (kind, name) = if uses_callerror {
1737 let ci_idx = state.current_ci_idx();
1738 let ci = state.get_ci(ci_idx).clone();
1739 let mut name: Option<Vec<u8>> = None;
1740 let kind = funcname_from_call(state, &ci, &mut name);
1741 if kind.is_some() {
1742 (kind.map(|k| k.to_vec()), name)
1743 } else {
1744 var_info_parts(state, val_idx)
1745 }
1746 } else {
1747 var_info_parts(state, val_idx)
1748 };
1749 typeerror_inner_parts(state, val, b"call", kind.as_deref(), name.as_deref())
1750}
1751
1752pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1755 if matches!(
1759 state.global().lua_version,
1760 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
1761 ) {
1762 let mut msg = Vec::new();
1763 msg.extend_from_slice(b"'for' ");
1764 msg.extend_from_slice(what);
1765 msg.extend_from_slice(b" must be a number");
1766 return prefixed_runtime(state, msg);
1767 }
1768 let t = crate::tagmethods::obj_type_name(state, val)
1769 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1770 let mut msg = Vec::new();
1771 msg.extend_from_slice(b"bad 'for' ");
1772 msg.extend_from_slice(what);
1773 msg.extend_from_slice(b" (number expected, got ");
1774 msg.extend_from_slice(&t);
1775 msg.push(b')');
1776 prefixed_runtime(state, msg)
1777}
1778
1779pub(crate) fn op_int_error(
1783 state: &LuaState,
1784 p1: &LuaValue,
1785 p1_idx: StackIdx,
1786 p2: &LuaValue,
1787 p2_idx: StackIdx,
1788 msg: &[u8],
1789) -> LuaError {
1790 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1792 (p1, p1_idx)
1793 } else {
1794 (p2, p2_idx)
1795 };
1796 type_error(state, bad_val, bad_idx, msg)
1797}
1798
1799pub(crate) fn to_int_error(
1805 state: &LuaState,
1806 p1: &LuaValue,
1807 p1_idx: Option<StackIdx>,
1808 _p2: &LuaValue,
1809 p2_idx: Option<StackIdx>,
1810) -> LuaError {
1811 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1812 p1_idx
1813 } else {
1814 p2_idx
1815 };
1816 let extra = match bad_idx {
1817 Some(idx) => var_info(state, idx),
1818 None => Vec::new(),
1819 };
1820 let mut msg = Vec::new();
1821 msg.extend_from_slice(b"number");
1822 msg.extend_from_slice(&extra);
1823 msg.extend_from_slice(b" has no integer representation");
1824 prefixed_runtime(state, msg)
1825}
1826
1827pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1830 let t1 = state.obj_type_name(p1);
1832 let t2 = state.obj_type_name(p2);
1833 let msg = if t1 == t2 {
1835 let mut m = Vec::new();
1836 m.extend_from_slice(b"attempt to compare two ");
1837 m.extend_from_slice(&t1);
1838 m.extend_from_slice(b" values");
1839 m
1840 } else {
1841 let mut m = Vec::new();
1842 m.extend_from_slice(b"attempt to compare ");
1843 m.extend_from_slice(&t1);
1844 m.extend_from_slice(b" with ");
1845 m.extend_from_slice(&t2);
1846 m
1847 };
1848 prefixed_runtime(state, msg)
1849}
1850
1851pub(crate) fn add_info(
1860 _state: Option<&mut LuaState>,
1861 msg: &[u8],
1862 src: Option<&LuaString>,
1863 line: i32,
1864 unknown_line_as_question: bool,
1865) -> Vec<u8> {
1866 let mut buff = [0u8; LUA_IDSIZE];
1868 if let Some(src) = src {
1869 chunk_id(&mut buff, src.as_bytes(), src.len());
1872 } else if unknown_line_as_question {
1873 let mut out = Vec::with_capacity(5 + msg.len());
1874 out.extend_from_slice(b"?:?: ");
1875 out.extend_from_slice(msg);
1876 return out;
1877 } else {
1878 buff[0] = b'?';
1879 }
1880 let src_part = buff
1883 .iter()
1884 .position(|&b| b == 0)
1885 .map_or(&buff[..], |n| &buff[..n]);
1886 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1887 out.extend_from_slice(src_part);
1888 out.push(b':');
1889 let line_str = line.to_string();
1891 out.extend_from_slice(line_str.as_bytes());
1892 out.extend_from_slice(b": ");
1893 out.extend_from_slice(msg);
1894 out
1895}
1896
1897fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1902 if p.lineinfo.is_empty() {
1903 return false;
1904 }
1905
1906 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1907 let mut delta: i32 = 0;
1908 let mut pc = oldpc;
1909 loop {
1910 pc += 1;
1911 if pc as usize >= p.lineinfo.len() {
1912 break;
1913 }
1914 let lineinfo = p.lineinfo[pc as usize];
1915 if lineinfo == ABS_LINE_INFO {
1916 break;
1917 }
1918 delta += lineinfo as i32;
1919 if pc == newpc {
1920 return delta != 0;
1921 }
1922 }
1923 }
1924 get_func_line(p, oldpc) != get_func_line(p, newpc)
1925}
1926
1927pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1933 let ci_idx = state.current_ci_idx();
1934 let ci = state.get_ci(ci_idx).clone();
1935 state.get_ci_mut(ci_idx).set_trap(true);
1936 let proto = ci_lua_proto(&ci, state);
1937
1938 if ci.saved_pc() == 0 {
1939 if proto.is_vararg {
1940 return Ok(0);
1941 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1942 state.hook_call(ci_idx)?;
1944 }
1945 }
1946 Ok(1)
1947}
1948
1949pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
1959 let ci_idx = state.current_ci_idx();
1960 let ci = state.get_ci(ci_idx).clone();
1961
1962 let mask = state.hook_mask();
1963
1964 if !state.allowhook {
1965 return Ok(1);
1966 }
1967
1968 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
1969 state.get_ci_mut(ci_idx).set_trap(false);
1970 return Ok(0);
1971 }
1972
1973 let next_pc = pc + 1;
1974 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
1975
1976 let counthook = if mask & LUA_MASKCOUNT != 0 {
1977 let hc = state.hook_count() - 1;
1978 state.set_hook_count(hc);
1979 hc == 0
1980 } else {
1981 false
1982 };
1983
1984 if counthook {
1985 state.reset_hook_count();
1986 } else if mask & LUA_MASKLINE == 0 {
1987 return Ok(1);
1988 }
1989
1990 if counthook {
1995 if let Some(err) = state.sandbox_charge_interval() {
1996 return Err(err);
1997 }
1998 }
1999
2000 if ci.callstatus & CIST_HOOKYIELD != 0 {
2001 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
2002 return Ok(1);
2003 }
2004
2005 if state.ci_lua_closure(ci_idx).is_none() {
2006 return Ok(1);
2007 }
2008
2009 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
2012 if !cur_instr.is_in_top() {
2013 let ci_top = state.get_ci(ci_idx).top;
2014 state.set_top(ci_top);
2015 }
2016
2017 if counthook {
2018 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
2020 }
2021
2022 if mask & LUA_MASKLINE != 0 {
2023 let proto = ci_lua_proto(&ci, state);
2024 let oldpc = if state.old_pc() < proto.code.len() as u32 {
2025 state.old_pc() as i32
2026 } else {
2027 0
2028 };
2029 let npci = next_pc as i32 - 1;
2031
2032 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
2033 let newline = get_func_line(&proto, npci);
2034 state.call_hook_event(LUA_HOOKLINE, newline)?;
2036 }
2037 state.set_old_pc(npci as u32);
2038 }
2039
2040 if state.status() == lua_types::status::LuaStatus::Yield {
2041 if counthook {
2042 state.set_hook_count(1);
2043 }
2044 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
2045 return Err(LuaError::Yield);
2047 }
2048
2049 Ok(1)
2050}
2051
2052fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
2060 out.fill(0);
2061 let n = crate::object::chunk_id(&mut out[..], source);
2062 if n < out.len() {
2063 out[n] = 0;
2064 }
2065}
2066
2067fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
2071 crate::func::get_local_name(p, n, pc)
2072}
2073
2074fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
2076 get_local_name(&cl.proto, n, pc)
2077}
2078
2079fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
2093 match state.get_at(ci.func) {
2094 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
2095 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
2096 }
2097}
2098
2099