1#[allow(unused_imports)] use crate::prelude::*;
11use crate::state::{
12 CallInfo, GcRef, LuaClosure, LuaClosureLua, LuaProto, LuaState, LuaTable, LuaValue,
13 CIST_FIN, CIST_HOOKED, CIST_HOOKYIELD, CIST_TAIL, CIST_TRAN,
14};
15use lua_types::{CallInfoIdx, StackIdx, LuaString};
16use lua_types::error::LuaError;
17use lua_types::opcode::Instruction;
18use crate::vm::InstructionExt;
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 prefixed = add_info(
86 None,
87 &msg,
88 src.map(|s| &**s),
89 line,
90 );
91 runtime_bytes(prefixed)
92}
93
94pub fn c_api_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
95 let ci_idx = state.current_ci_idx();
96 if let Some(parent_idx) = state.prev_ci(ci_idx) {
97 let parent_ci = state.get_ci(parent_idx).clone();
98 if parent_ci.is_lua() {
99 let proto = ci_lua_proto(&parent_ci, state);
100 let src = proto.source_string();
101 let line = get_current_line(&parent_ci, state);
102 let prefixed = add_info(None, &msg, src.map(|s| &**s), line);
103 return runtime_bytes(prefixed);
104 }
105 }
106 runtime_bytes(msg)
107}
108
109#[allow(dead_code)]
119fn find_func_in_table(table: &LuaTable, target: &LuaValue, prefix: &[u8], depth: u8) -> Option<Vec<u8>> {
120 let mut key = LuaValue::Nil;
121 loop {
122 let (k, v) = match table.next_pair(&key) {
123 Some(pair) => pair,
124 None => break,
125 };
126 if !matches!(v, LuaValue::Nil) {
127 let key_bytes: Option<Vec<u8>> = match &k {
128 LuaValue::Str(s) => Some(s.as_bytes().to_vec()),
129 _ => None,
130 };
131 if let Some(kb) = key_bytes {
132 if &v == target {
133 if prefix.is_empty() {
134 return Some(kb);
135 }
136 let mut result = prefix.to_vec();
137 result.push(b'.');
138 result.extend_from_slice(&kb);
139 return Some(result);
140 }
141 if depth > 0 {
142 if let LuaValue::Table(sub) = &v {
143 let new_prefix = if prefix.is_empty() {
144 kb.clone()
145 } else {
146 let mut p = prefix.to_vec();
147 p.push(b'.');
148 p.extend_from_slice(&kb);
149 p
150 };
151 if let Some(name) = find_func_in_table(&**sub, target, &new_prefix, depth - 1) {
152 return Some(name);
153 }
154 }
155 }
156 }
157 }
158 key = k;
159 }
160 None
161}
162
163#[allow(dead_code)]
171fn find_func_name_in_globals(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
172 let globals = state.global().globals.clone();
173 if let LuaValue::Table(globals_table) = globals {
174 find_func_in_table(&*globals_table, func_val, b"", 1)
175 } else {
176 None
177 }
178}
179
180fn find_func_name_in_loaded(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
187 let registry = state.global().l_registry.clone();
188 let loaded = match registry {
189 LuaValue::Table(ref reg_table) => reg_table.get_str_bytes(b"_LOADED"),
190 _ => return None,
191 };
192 let loaded_table = match loaded {
193 LuaValue::Table(t) => t,
194 _ => return None,
195 };
196 find_func_in_table(&*loaded_table, func_val, b"", 1)
197}
198
199pub fn arg_error_impl(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> LuaError {
203 let mut ar = LuaDebug::default();
204 if !get_stack(state, 0, &mut ar) {
205 let msg = format!("bad argument #{} ({})", arg, String::from_utf8_lossy(extramsg));
206 return c_api_runtime(state, msg.into_bytes());
207 }
208 get_info(state, b"n", &mut ar);
209 if ar.namewhat.as_deref() == Some(b"method") {
210 arg -= 1;
211 if arg == 0 {
212 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
213 let msg = format!(
214 "calling '{}' on bad self ({})",
215 String::from_utf8_lossy(&name),
216 String::from_utf8_lossy(extramsg)
217 );
218 return c_api_runtime(state, msg.into_bytes());
219 }
220 }
221 let fname = ar.name.clone().or_else(|| {
222 let ci_idx = ar.i_ci?;
223 let func_slot = state.get_ci(ci_idx).func;
224 let func_val = state.get_at(func_slot).clone();
225 let found = find_func_name_in_loaded(state, &func_val)?;
226 if found.starts_with(b"_G.") {
227 Some(found[3..].to_vec())
228 } else {
229 Some(found)
230 }
231 }).unwrap_or_else(|| b"?".to_vec());
232 let msg = format!(
233 "bad argument #{} to '{}' ({})",
234 arg,
235 String::from_utf8_lossy(&fname),
236 String::from_utf8_lossy(extramsg)
237 );
238 c_api_runtime(state, msg.into_bytes())
239}
240
241pub struct LuaDebug {
252 pub event: i32,
253 pub name: Option<Vec<u8>>,
254 pub namewhat: Option<&'static [u8]>,
255 pub what: Option<&'static [u8]>,
256 pub source: Option<Vec<u8>>,
257 pub srclen: usize,
258 pub currentline: i32,
259 pub linedefined: i32,
260 pub lastlinedefined: i32,
261 pub nups: u8,
262 pub nparams: u8,
263 pub isvararg: bool,
264 pub istailcall: bool,
265 pub ftransfer: u16,
266 pub ntransfer: u16,
267 pub short_src: [u8; LUA_IDSIZE],
268 pub i_ci: Option<CallInfoIdx>,
270}
271
272impl Default for LuaDebug {
273 fn default() -> Self {
274 LuaDebug {
275 event: 0,
276 name: None,
277 namewhat: None,
278 what: None,
279 source: None,
280 srclen: 0,
281 currentline: -1,
282 linedefined: -1,
283 lastlinedefined: -1,
284 nups: 0,
285 nparams: 0,
286 isvararg: false,
287 istailcall: false,
288 ftransfer: 0,
289 ntransfer: 0,
290 short_src: [0u8; LUA_IDSIZE],
291 i_ci: None,
292 }
293 }
294}
295
296#[inline]
300fn is_lua_closure(cl: Option<&LuaClosure>) -> bool {
301 matches!(cl, Some(LuaClosure::Lua(_)))
302}
303
304fn current_pc(ci: &CallInfo) -> i32 {
319 debug_assert!(ci.is_lua());
320 ci.saved_pc().saturating_sub(1) as i32
323}
324
325fn get_baseline(f: &LuaProto, pc: i32, basepc: &mut i32) -> i32 {
333 if f.abslineinfo.is_empty() || pc < f.abslineinfo[0].pc {
334 *basepc = -1;
335 return f.linedefined;
336 }
337 let mut i = (pc as u32 / MAX_IWTH_ABS as u32).saturating_sub(1) as usize;
339 debug_assert!(
340 i < f.abslineinfo.len() && f.abslineinfo[i].pc <= pc,
341 "getbaseline: estimate is not a lower bound"
342 );
343 while i + 1 < f.abslineinfo.len() && pc >= f.abslineinfo[i + 1].pc {
344 i += 1;
345 }
346 *basepc = f.abslineinfo[i].pc;
347 f.abslineinfo[i].line
348}
349
350pub(crate) fn get_func_line(f: &LuaProto, pc: i32) -> i32 {
354 if f.lineinfo.is_empty() {
355 return -1;
356 }
357 let mut basepc: i32 = 0;
358 let mut baseline = get_baseline(f, pc, &mut basepc);
359 while basepc < pc {
362 basepc += 1;
363 debug_assert!(
364 f.lineinfo[basepc as usize] != ABS_LINE_INFO,
365 "get_func_line: hit ABSLINEINFO in incremental walk"
366 );
367 baseline += f.lineinfo[basepc as usize] as i32;
368 }
369 baseline
370}
371
372fn get_current_line(ci: &CallInfo, state: &LuaState) -> i32 {
375 let proto = ci_lua_proto(ci, state);
376 get_func_line(&proto, current_pc(ci))
377}
378
379pub(crate) fn arm_traps(state: &mut LuaState) {
391 set_traps(state);
392}
393
394fn set_traps(state: &mut LuaState) {
395 for ci in state.call_stack_mut().iter_mut() {
399 if ci.is_lua() {
400 ci.set_trap(true);
401 }
402 }
403}
404
405pub fn set_hook(
408 state: &mut LuaState,
409 func: Option<Box<dyn FnMut(&mut LuaState, &LuaDebug)>>,
410 mask: i32,
411 count: i32,
412) {
413 let (func, mask) = if func.is_none() || mask == 0 {
414 (None, 0i32)
415 } else {
416 (func, mask)
417 };
418 state.set_hook(func);
419 state.set_base_hook_count(count);
420 state.reset_hook_count();
422 state.set_hook_mask(mask as u8);
424 if mask != 0 {
425 set_traps(state);
426 }
427}
428
429pub fn get_hook_installed(state: &LuaState) -> bool {
436 state.hook().is_some()
437}
438
439pub fn get_hook_mask(state: &LuaState) -> i32 {
442 state.hook_mask() as i32
443}
444
445pub fn get_hook_count(state: &LuaState) -> i32 {
448 state.base_hook_count()
449}
450
451pub fn get_stack(state: &LuaState, level: i32, ar: &mut LuaDebug) -> bool {
458 if level < 0 {
459 return false;
460 }
461 let mut remaining = level;
462 let mut ci_idx = state.current_ci_idx();
463 loop {
464 if remaining == 0 {
465 break;
466 }
467 match state.prev_ci(ci_idx) {
468 Some(prev) => {
469 ci_idx = prev;
470 remaining -= 1;
471 }
472 None => {
473 return false;
474 }
475 }
476 }
477 if !state.is_base_ci(ci_idx) {
478 ar.i_ci = Some(ci_idx);
479 true
480 } else {
481 false
482 }
483}
484
485fn upval_name(p: &LuaProto, uv: usize) -> &[u8] {
490 debug_assert!(uv < p.upvalues.len(), "upval_name: index out of range");
493 p.upvalues[uv].name.as_ref().map_or(b"?" as &[u8], |s| s.as_bytes())
496}
497
498fn find_vararg(state: &LuaState, ci: &CallInfo, n: i32) -> Option<(StackIdx, &'static [u8])> {
505 let proto = ci_lua_proto(ci, state);
506 if proto.is_vararg {
507 let nextra = ci.nextra_args();
508 if n >= -(nextra as i32) {
509 let pos = ci.func - (nextra + n + 1);
512 return Some((pos, b"(vararg)" as &[u8]));
513 }
514 }
515 None
516}
517
518pub(crate) fn find_local(
531 state: &LuaState,
532 ci_idx: CallInfoIdx,
533 n: i32,
534 pos: Option<&mut StackIdx>,
535) -> Option<Vec<u8>> {
536 let ci = state.get_ci(ci_idx);
537 let base = ci.func + 1;
538 let mut name: Option<Vec<u8>> = None;
539
540 if ci.is_lua() {
541 if n < 0 {
542 if let Some((vpos, vname)) = find_vararg(state, ci, n) {
543 if let Some(out_pos) = pos {
544 *out_pos = vpos;
545 }
546 return Some(vname.to_vec());
547 }
548 return None;
549 } else {
550 let proto = ci_lua_proto(ci, state);
551 let pc = current_pc(ci);
552 name = crate::func::get_local_name(&proto, n, pc).map(|s| s.to_vec());
553 }
554 }
555
556 if name.is_none() {
557 let limit: u32 = if ci_idx == state.current_ci_idx() {
558 state.top_idx().0
559 } else {
560 ci.next
561 .map(|next| state.get_ci(next).func.0)
562 .unwrap_or_else(|| state.top_idx().0)
563 };
564 if n > 0 && limit.saturating_sub(base.0) >= n as u32 {
565 name = Some(if ci.is_lua() { b"(temporary)".to_vec() } else { b"(C temporary)".to_vec() });
566 } else {
567 return None;
568 }
569 }
570
571 if let Some(out_pos) = pos {
572 *out_pos = base + (n - 1);
573 }
574 name
575}
576
577pub fn get_local(state: &mut LuaState, ar: Option<&LuaDebug>, n: i32) -> Option<Vec<u8>> {
582 if ar.is_none() {
583 let top_val = state.peek_top();
585 if !matches!(top_val, LuaValue::Function(LuaClosure::Lua(_))) {
586 return None;
587 }
588 let name_owned: Option<Vec<u8>> = {
591 let cl = match top_val {
592 LuaValue::Function(LuaClosure::Lua(ref cl)) => cl.clone(),
593 _ => unreachable!(),
594 };
595 get_local_name_from_closure(&cl, n, 0).map(|s| s.to_vec())
597 };
598 return name_owned;
599 }
600
601 let ar = ar.unwrap();
602 let ci_idx = ar.i_ci?;
603 let mut pos = StackIdx(0);
604 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
607
608 if name_owned.is_some() {
609 let val = state.get_at(pos).clone();
610 state.push(val);
611 }
612 name_owned
613}
614
615pub fn set_local(state: &mut LuaState, ar: &LuaDebug, n: i32) -> Option<Vec<u8>> {
619 let ci_idx = ar.i_ci?;
620 let mut pos = StackIdx(0);
621 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
623 if name_owned.is_some() {
624 let val = state.get_at(state.top_idx() - 1).clone();
625 state.set_at(pos, val);
626 state.pop_n(1);
627 }
628 name_owned
629}
630
631fn func_info(ar: &mut LuaDebug, cl: Option<&LuaClosure>) {
636 if !is_lua_closure(cl) {
637 ar.source = Some(b"=[C]".to_vec());
639 ar.srclen = b"=[C]".len();
640 ar.linedefined = -1;
641 ar.lastlinedefined = -1;
642 ar.what = Some(b"C");
643 } else {
644 let lua_cl = match cl {
645 Some(LuaClosure::Lua(cl)) => cl,
646 _ => unreachable!(),
647 };
648 let proto: &LuaProto = &lua_cl.proto;
650 if let Some(src) = proto.source_string() {
652 ar.source = Some(src.as_bytes().to_vec());
653 ar.srclen = src.as_bytes().len();
654 } else {
655 ar.source = Some(b"=?".to_vec());
656 ar.srclen = b"=?".len();
657 }
658 ar.linedefined = proto.linedefined;
659 ar.lastlinedefined = proto.lastlinedefined;
660 ar.what = Some(if ar.linedefined == 0 { b"main" } else { b"Lua" });
661 }
662 chunk_id(&mut ar.short_src, ar.source.as_deref().unwrap_or(b"?"), ar.srclen);
664}
665
666fn next_line(p: &LuaProto, currentline: i32, pc: usize) -> i32 {
670 if p.lineinfo.get(pc).copied() != Some(ABS_LINE_INFO) {
672 currentline + p.lineinfo[pc] as i32
673 } else {
674 get_func_line(p, pc as i32)
675 }
676}
677
678fn collect_valid_lines(state: &mut LuaState, cl: Option<&LuaClosure>) -> Result<(), LuaError> {
682 if !is_lua_closure(cl) {
683 state.push(LuaValue::Nil);
685 return Ok(());
686 }
687 let lua_cl = match cl {
688 Some(LuaClosure::Lua(cl)) => cl.clone(),
689 _ => unreachable!(),
690 };
691 let proto: GcRef<LuaProto> = lua_cl.proto.clone();
693 let p: &LuaProto = &proto;
694
695 let mut currentline = p.linedefined;
696
697 let t = state.new_table();
699 state.push(LuaValue::Table(t.clone()));
701
702 if !p.lineinfo.is_empty() {
703 let v = LuaValue::Bool(true);
705
706 let start_i = if !p.is_vararg {
707 0usize
708 } else {
709 debug_assert!(
711 p.code.first().map(|i| i.is_vararg_prep()).unwrap_or(false),
712 "collect_valid_lines: first instruction of vararg should be OP_VARARGPREP"
713 );
714 currentline = next_line(p, currentline, 0);
715 1usize
716 };
717
718 for i in start_i..p.lineinfo.len() {
720 currentline = next_line(p, currentline, i);
721 t.raw_set_int(state, currentline as i64, v.clone())?;
723 }
724 }
725 Ok(())
726}
727
728fn get_func_name<'a>(
734 state: &'a LuaState,
735 ci: Option<&CallInfo>,
736 name: &mut Option<Vec<u8>>,
737) -> Option<&'static [u8]> {
738 let ci = ci?;
741 if ci.callstatus & CIST_TAIL != 0 {
742 return None;
743 }
744 let prev_idx = ci.previous?;
747 let prev_ci = state.get_ci(prev_idx).clone();
748 funcname_from_call(state, &prev_ci, name)
749}
750
751fn aux_get_info(
754 state: &LuaState,
755 what: &[u8],
756 ar: &mut LuaDebug,
757 cl: Option<&LuaClosure>,
758 ci: Option<&CallInfo>,
759) -> bool {
760 let mut status = true;
761 for &ch in what {
762 match ch {
763 b'S' => {
764 func_info(ar, cl);
765 }
766 b'l' => {
767 ar.currentline = match ci {
768 Some(ci) if ci.is_lua() => get_current_line(ci, state),
769 _ => -1,
770 };
771 }
772 b'u' => {
773 ar.nups = cl.map_or(0, |c| c.nupvalues() as u8);
774 match cl {
775 Some(LuaClosure::Lua(lua_cl)) => {
776 ar.isvararg = lua_cl.proto.is_vararg;
778 ar.nparams = lua_cl.proto.numparams;
779 }
780 _ => {
781 ar.isvararg = true;
782 ar.nparams = 0;
783 }
784 }
785 }
786 b't' => {
787 ar.istailcall = ci.map_or(false, |ci| ci.callstatus & CIST_TAIL != 0);
788 }
789 b'n' => {
790 let mut name: Option<Vec<u8>> = None;
791 ar.namewhat = get_func_name(state, ci, &mut name);
792 if ar.namewhat.is_none() {
793 ar.namewhat = Some(b"");
794 ar.name = None;
795 } else {
796 ar.name = name;
797 }
798 }
799 b'r' => match ci {
801 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
802 ar.ftransfer = ci.transfer_ftransfer();
804 ar.ntransfer = ci.transfer_ntransfer();
805 }
806 _ => {
807 ar.ftransfer = 0;
808 ar.ntransfer = 0;
809 }
810 },
811 b'L' | b'f' => {}
812 _ => {
813 status = false;
814 }
815 }
816 }
817 status
818}
819
820pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
823 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
824 let func_val = state.peek_at(state.top_idx() - 1).clone();
825 state.pop_n(1);
826 debug_assert!(
827 matches!(func_val, LuaValue::Function(_)),
828 "get_info: function expected"
829 );
830 let cl = match &func_val {
831 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => {
832 Some(match &func_val {
833 LuaValue::Function(c) => c.clone(),
834 _ => unreachable!(),
835 })
836 }
837 _ => None,
838 };
839 (cl, None, func_val, &what[1..])
840 } else {
841 let ci_idx = match ar.i_ci { Some(i) => i, None => return false };
842 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
843 debug_assert!(
844 matches!(func_val, LuaValue::Function(_)),
845 "get_info: non-function at ci->func"
846 );
847 let cl = match &func_val {
848 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => {
849 Some(match &func_val {
850 LuaValue::Function(c) => c.clone(),
851 _ => unreachable!(),
852 })
853 }
854 _ => None,
855 };
856 (cl, Some(ci_idx), func_val, what)
857 };
858
859 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
860 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
861
862 if what.contains(&b'f') {
863 state.push(func_val);
864 }
865 if what.contains(&b'L') {
866 let _ = collect_valid_lines(state, cl.as_ref());
868 }
869 status
870}
871
872#[inline]
878fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
879 if pc < jmptarget { -1 } else { pc }
880}
881
882fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
886 let mut setreg: i32 = -1;
887 let mut jmptarget: i32 = 0;
888
889 let effective_lastpc = if p.code.get(lastpc as usize).map_or(false, |i| i.is_mm_mode()) {
892 lastpc - 1
893 } else {
894 lastpc
895 };
896
897 for pc in 0..effective_lastpc {
898 let instr = p.code[pc as usize];
899 let op = instr.opcode();
900 let a = instr.arg_a() as i32;
901
902 let change = match op {
903 OpCode::LoadNil => {
904 let b = instr.arg_b() as i32;
905 a <= reg && reg <= a + b
906 }
907 OpCode::TForCall => reg >= a + 2,
908 OpCode::Call | OpCode::TailCall => reg >= a,
909 OpCode::Jmp => {
910 let b = instr.arg_s_j();
911 let dest = pc + 1 + b;
912 if dest <= effective_lastpc && dest > jmptarget {
913 jmptarget = dest;
914 }
915 false
916 }
917 _ => {
918 instr.test_a_mode() && reg == a
921 }
922 };
923
924 if change {
925 setreg = filter_pc(pc, jmptarget);
926 }
927 }
928 setreg
929}
930
931fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
936 match p.k.get(index) {
939 Some(LuaValue::Str(s)) => {
940 *name = s.as_bytes();
942 Some(b"constant")
943 }
944 _ => {
945 *name = b"?";
946 None
947 }
948 }
949}
950
951fn basic_get_obj_name<'a>(
955 p: &'a LuaProto,
956 ppc: &mut i32,
957 reg: i32,
958 name: &mut &'a [u8],
959) -> Option<&'static [u8]> {
960 let pc = *ppc;
961 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
963 *name = local_name;
964 return Some(b"local");
965 }
966
967 *ppc = find_set_reg(p, pc, reg);
968 let pc = *ppc;
969
970 if pc == -1 {
971 return None;
972 }
973
974 let instr = p.code[pc as usize];
975 let op = instr.opcode();
976 match op {
977 OpCode::Move => {
978 let b = instr.arg_b() as i32;
979 if b < instr.arg_a() as i32 {
980 return basic_get_obj_name(p, ppc, b, name);
981 }
982 }
983 OpCode::GetUpVal => {
984 *name = upval_name(p, instr.arg_b() as usize);
985 return Some(b"upvalue");
986 }
987 OpCode::LoadK => {
988 return kname(p, instr.arg_bx() as usize, name);
989 }
990 OpCode::LoadKx => {
991 let next = p.code[(pc + 1) as usize];
992 return kname(p, next.arg_ax() as usize, name);
993 }
994 _ => {}
995 }
996 None
997}
998
999fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
1003 let mut pc = pc;
1004 let what = basic_get_obj_name(p, &mut pc, c, name);
1006 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1007 *name = b"?";
1008 }
1009}
1010
1011fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1014 let c = instr.arg_c() as i32;
1015 if instr.arg_k() != 0 {
1017 kname(p, c as usize, name);
1018 } else {
1019 rname(p, pc, c, name);
1020 }
1021}
1022
1023fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1027 let t = instr.arg_b() as usize;
1028 let mut name: &[u8] = b"?";
1029 if isup {
1030 name = upval_name(p, t);
1031 } else {
1032 let mut pc = pc;
1033 basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1034 }
1035 if name == LUA_ENV { b"global" } else { b"field" }
1036}
1037
1038fn get_obj_name<'a>(
1042 p: &'a LuaProto,
1043 lastpc: i32,
1044 reg: i32,
1045 name: &mut &'a [u8],
1046) -> Option<&'static [u8]> {
1047 let mut lastpc = lastpc;
1048 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1049 if kind.is_some() {
1050 return kind;
1051 }
1052
1053 if lastpc == -1 {
1054 return None;
1055 }
1056
1057 let instr = p.code[lastpc as usize];
1058 let op = instr.opcode();
1059 match op {
1060 OpCode::GetTabUp => {
1061 let k = instr.arg_c() as usize;
1062 kname(p, k, name);
1063 Some(is_env(p, lastpc, instr, true))
1064 }
1065 OpCode::GetTable => {
1066 let k = instr.arg_c() as i32;
1067 rname(p, lastpc, k, name);
1068 Some(is_env(p, lastpc, instr, false))
1069 }
1070 OpCode::GetI => {
1071 *name = b"integer index";
1072 Some(b"field")
1073 }
1074 OpCode::GetField => {
1075 let k = instr.arg_c() as usize;
1076 kname(p, k, name);
1077 Some(is_env(p, lastpc, instr, false))
1078 }
1079 OpCode::Self_ => {
1080 rkname(p, lastpc, instr, name);
1081 Some(b"method")
1082 }
1083 _ => None,
1084 }
1085}
1086
1087fn funcname_from_code<'a>(
1094 state: &LuaState,
1095 p: &'a LuaProto,
1096 pc: i32,
1097 name: &mut Option<Vec<u8>>,
1098) -> Option<&'static [u8]> {
1099 let instr = p.code[pc as usize];
1100 let op = instr.opcode();
1101
1102 match op {
1103 OpCode::Call | OpCode::TailCall => {
1104 let mut name_bytes: &[u8] = b"?";
1105 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1106 *name = Some(name_bytes.to_vec());
1107 kind
1108 }
1109 OpCode::TForCall => {
1110 *name = Some(b"for iterator".to_vec());
1111 Some(b"for iterator")
1112 }
1113 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1115 get_tm_name(state, TagMethod::Index, name)
1116 }
1117 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1118 get_tm_name(state, TagMethod::NewIndex, name)
1119 }
1120 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1121 let tm_idx = instr.arg_c() as u8;
1124 let tm = TagMethod::from_u8(tm_idx);
1125 get_tm_name(state, tm, name)
1126 }
1127 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1128 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1129 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1130 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1131 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1132 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1133 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1134 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1135 _ => None,
1136 }
1137}
1138
1139fn get_tm_name(
1145 state: &LuaState,
1146 tm: TagMethod,
1147 name: &mut Option<Vec<u8>>,
1148) -> Option<&'static [u8]> {
1149 let raw_bytes: Vec<u8> = state.global()
1153 .tm_name(tm)
1154 .map(|s| s.as_bytes().to_vec())
1155 .unwrap_or_default();
1156 let stripped = raw_bytes
1157 .strip_prefix(b"__")
1158 .unwrap_or(&raw_bytes)
1159 .to_vec();
1160 *name = Some(stripped);
1161 Some(b"metamethod")
1162}
1163
1164fn funcname_from_call<'a>(
1167 state: &'a LuaState,
1168 ci: &CallInfo,
1169 name: &mut Option<Vec<u8>>,
1170) -> Option<&'static [u8]> {
1171 if ci.callstatus & CIST_HOOKED != 0 {
1172 *name = Some(b"?".to_vec());
1173 return Some(b"hook");
1174 }
1175 if ci.callstatus & CIST_FIN != 0 {
1176 *name = Some(b"__gc".to_vec());
1177 return Some(b"metamethod");
1178 }
1179 if ci.is_lua() {
1180 let proto = ci_lua_proto(ci, state);
1181 return funcname_from_code(state, &proto, current_pc(ci), name);
1182 }
1183 None
1184}
1185
1186fn in_stack(ci: &CallInfo, val_idx: StackIdx) -> i32 {
1197 let base = StackIdx(ci.func.0 + 1);
1198 let ci_top = ci.top;
1201 let mut pos = 0i32;
1202 let mut cur = base;
1203 while cur.0 < ci_top.0 {
1204 if cur == val_idx {
1205 return pos;
1206 }
1207 cur = StackIdx(cur.0 + 1);
1208 pos += 1;
1209 }
1210 -1
1211}
1212
1213fn get_upval_name<'a>(
1222 ci: &CallInfo,
1223 val_idx: StackIdx,
1224 name: &mut &'a [u8],
1225 state: &'a LuaState,
1226) -> Option<&'static [u8]> {
1227 let proto = ci_lua_proto(ci, state);
1228 let lua_cl = match state.get_at(ci.func) {
1231 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1232 _ => return None,
1233 };
1234 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1235 let upval = upval_slot.get();
1236 let state = upval.slot().clone();
1237 if let lua_types::UpValState::Open { idx, .. } = state {
1238 if idx == val_idx {
1239 let _ = upval_name(&proto, i);
1242 *name = b"upvalue";
1243 return Some(b"upvalue");
1244 }
1245 }
1246 }
1247 None
1248}
1249
1250fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1255 match (kind, name) {
1256 (Some(k), Some(n)) => {
1257 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1258 out.extend_from_slice(b" (");
1259 out.extend_from_slice(k);
1260 out.extend_from_slice(b" '");
1261 out.extend_from_slice(n);
1262 out.extend_from_slice(b"')");
1263 out
1264 }
1265 _ => Vec::new(),
1266 }
1267}
1268
1269fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1273 let ci_idx = state.current_ci_idx();
1274 let ci = state.get_ci(ci_idx).clone();
1275 let mut kind: Option<&[u8]> = None;
1276 let mut name_owned: Vec<u8> = b"?".to_vec();
1277
1278 if ci.is_lua() {
1279 let mut up_name: &[u8] = b"?";
1280 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1281 if kind.is_some() {
1282 name_owned = up_name.to_vec();
1283 } else {
1284 let reg = in_stack(&ci, val_idx);
1285 if reg >= 0 {
1286 let proto = ci_lua_proto(&ci, state);
1287 let mut nref: &[u8] = b"?";
1288 let pc = current_pc(&ci);
1289 let k = get_obj_name(&proto, pc, reg, &mut nref);
1290 kind = k;
1291 if kind.is_some() {
1292 name_owned = nref.to_vec();
1293 }
1294 }
1295 }
1296 }
1297 format_var_info(kind, if kind.is_some() { Some(&name_owned) } else { None })
1298}
1299
1300fn typeerror_inner(
1305 state: &LuaState,
1306 val: &LuaValue,
1307 op: &[u8],
1308 extra: &[u8],
1309) -> LuaError {
1310 let t = state.obj_type_name(val);
1311 let mut msg = Vec::new();
1312 msg.extend_from_slice(b"attempt to ");
1313 msg.extend_from_slice(op);
1314 msg.extend_from_slice(b" a ");
1315 msg.extend_from_slice(&t);
1316 msg.extend_from_slice(b" value");
1317 msg.extend_from_slice(extra);
1318 prefixed_runtime(state, msg)
1319}
1320
1321pub(crate) fn type_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx, op: &[u8]) -> LuaError {
1325 let extra = var_info(state, val_idx);
1326 typeerror_inner(state, val, op, &extra)
1327}
1328
1329pub(crate) fn type_error_with_hint(
1335 state: &LuaState,
1336 val: &LuaValue,
1337 op: &[u8],
1338 kind: &[u8],
1339 name: &[u8],
1340) -> LuaError {
1341 let extra = format_var_info(Some(kind), Some(name));
1342 let t = obj_type_name_static(val);
1343 let mut msg = Vec::new();
1344 msg.extend_from_slice(b"attempt to ");
1345 msg.extend_from_slice(op);
1346 msg.extend_from_slice(b" a ");
1347 msg.extend_from_slice(t);
1348 msg.extend_from_slice(b" value");
1349 msg.extend_from_slice(&extra);
1350 prefixed_runtime(state, msg)
1351}
1352
1353fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1356 match val {
1357 LuaValue::Nil => b"nil",
1358 LuaValue::Bool(_) => b"boolean",
1359 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1360 LuaValue::Str(_) => b"string",
1361 LuaValue::Table(_) => b"table",
1362 LuaValue::Function(_) => b"function",
1363 LuaValue::UserData(_) => b"userdata",
1364 LuaValue::LightUserData(_) => b"light userdata",
1365 LuaValue::Thread(_) => b"thread",
1366 }
1367}
1368
1369pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1373 let ci_idx = state.current_ci_idx();
1374 let ci = state.get_ci(ci_idx).clone();
1375 let mut name: Option<Vec<u8>> = None;
1376 let kind = funcname_from_call(state, &ci, &mut name);
1377 let extra = if kind.is_some() {
1378 format_var_info(kind, name.as_deref())
1379 } else {
1380 var_info(state, val_idx)
1381 };
1382 typeerror_inner(state, val, b"call", &extra)
1383}
1384
1385pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1388 let t = crate::tagmethods::obj_type_name(state, val)
1389 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1390 let mut msg = Vec::new();
1391 msg.extend_from_slice(b"bad 'for' ");
1392 msg.extend_from_slice(what);
1393 msg.extend_from_slice(b" (number expected, got ");
1394 msg.extend_from_slice(&t);
1395 msg.push(b')');
1396 prefixed_runtime(state, msg)
1397}
1398
1399pub(crate) fn op_int_error(
1403 state: &LuaState,
1404 p1: &LuaValue,
1405 p1_idx: StackIdx,
1406 p2: &LuaValue,
1407 p2_idx: StackIdx,
1408 msg: &[u8],
1409) -> LuaError {
1410 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1412 (p1, p1_idx)
1413 } else {
1414 (p2, p2_idx)
1415 };
1416 type_error(state, bad_val, bad_idx, msg)
1417}
1418
1419pub(crate) fn to_int_error(
1425 state: &LuaState,
1426 p1: &LuaValue,
1427 p1_idx: Option<StackIdx>,
1428 _p2: &LuaValue,
1429 p2_idx: Option<StackIdx>,
1430) -> LuaError {
1431 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1432 p1_idx
1433 } else {
1434 p2_idx
1435 };
1436 let extra = match bad_idx {
1437 Some(idx) => var_info(state, idx),
1438 None => Vec::new(),
1439 };
1440 let mut msg = Vec::new();
1441 msg.extend_from_slice(b"number");
1442 msg.extend_from_slice(&extra);
1443 msg.extend_from_slice(b" has no integer representation");
1444 prefixed_runtime(state, msg)
1445}
1446
1447pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1450 let t1 = state.obj_type_name(p1);
1452 let t2 = state.obj_type_name(p2);
1453 let msg = if t1 == t2 {
1455 let mut m = Vec::new();
1456 m.extend_from_slice(b"attempt to compare two ");
1457 m.extend_from_slice(&t1);
1458 m.extend_from_slice(b" values");
1459 m
1460 } else {
1461 let mut m = Vec::new();
1462 m.extend_from_slice(b"attempt to compare ");
1463 m.extend_from_slice(&t1);
1464 m.extend_from_slice(b" with ");
1465 m.extend_from_slice(&t2);
1466 m
1467 };
1468 prefixed_runtime(state, msg)
1469}
1470
1471pub(crate) fn add_info(
1480 _state: Option<&mut LuaState>,
1481 msg: &[u8],
1482 src: Option<&LuaString>,
1483 line: i32,
1484) -> Vec<u8> {
1485 let mut buff = [0u8; LUA_IDSIZE];
1487 if let Some(src) = src {
1488 chunk_id(&mut buff, src.as_bytes(), src.len());
1491 } else {
1492 buff[0] = b'?';
1493 }
1494 let src_part = buff.iter().position(|&b| b == 0).map_or(&buff[..], |n| &buff[..n]);
1497 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1498 out.extend_from_slice(src_part);
1499 out.push(b':');
1500 let line_str = line.to_string();
1502 out.extend_from_slice(line_str.as_bytes());
1503 out.extend_from_slice(b": ");
1504 out.extend_from_slice(msg);
1505 out
1506}
1507
1508
1509fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1514 if p.lineinfo.is_empty() {
1515 return false;
1516 }
1517
1518 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1519 let mut delta: i32 = 0;
1520 let mut pc = oldpc;
1521 loop {
1522 pc += 1;
1523 if pc as usize >= p.lineinfo.len() {
1524 break;
1525 }
1526 let lineinfo = p.lineinfo[pc as usize];
1527 if lineinfo == ABS_LINE_INFO {
1528 break;
1529 }
1530 delta += lineinfo as i32;
1531 if pc == newpc {
1532 return delta != 0;
1533 }
1534 }
1535 }
1536 get_func_line(p, oldpc) != get_func_line(p, newpc)
1537}
1538
1539pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1545 let ci_idx = state.current_ci_idx();
1546 let ci = state.get_ci(ci_idx).clone();
1547 state.get_ci_mut(ci_idx).set_trap(true);
1548 let proto = ci_lua_proto(&ci, state);
1549
1550 if ci.saved_pc() == 0 {
1551 if proto.is_vararg {
1552 return Ok(0);
1553 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1554 state.hook_call(ci_idx)?;
1556 }
1557 }
1558 Ok(1)
1559}
1560
1561pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
1571 let ci_idx = state.current_ci_idx();
1572 let ci = state.get_ci(ci_idx).clone();
1573
1574 let mask = state.hook_mask();
1575
1576 if !state.allowhook {
1577 return Ok(1);
1578 }
1579
1580 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
1581 state.get_ci_mut(ci_idx).set_trap(false);
1582 return Ok(0);
1583 }
1584
1585 let next_pc = pc + 1;
1586 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
1587
1588 let counthook = if mask & LUA_MASKCOUNT != 0 {
1589 let hc = state.hook_count() - 1;
1590 state.set_hook_count(hc);
1591 hc == 0
1592 } else {
1593 false
1594 };
1595
1596 if counthook {
1597 state.reset_hook_count();
1598 } else if mask & LUA_MASKLINE == 0 {
1599 return Ok(1);
1600 }
1601
1602 if counthook {
1607 if let Some(err) = state.sandbox_charge_interval() {
1608 return Err(err);
1609 }
1610 }
1611
1612 if ci.callstatus & CIST_HOOKYIELD != 0 {
1613 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
1614 return Ok(1);
1615 }
1616
1617 if state.ci_lua_closure(ci_idx).is_none() {
1618 return Ok(1);
1619 }
1620
1621 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
1624 if !cur_instr.is_in_top() {
1625 let ci_top = state.get_ci(ci_idx).top;
1626 state.set_top(ci_top);
1627 }
1628
1629 if counthook {
1630 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
1632 }
1633
1634 if mask & LUA_MASKLINE != 0 {
1635 let proto = ci_lua_proto(&ci, state);
1636 let oldpc = if state.old_pc() < proto.code.len() as u32 {
1637 state.old_pc() as i32
1638 } else {
1639 0
1640 };
1641 let npci = next_pc as i32 - 1;
1643
1644 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
1645 let newline = get_func_line(&proto, npci);
1646 state.call_hook_event(LUA_HOOKLINE, newline)?;
1648 }
1649 state.set_old_pc(npci as u32);
1650 }
1651
1652 if state.status() == lua_types::status::LuaStatus::Yield {
1653 if counthook {
1654 state.set_hook_count(1);
1655 }
1656 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
1657 return Err(LuaError::Yield);
1659 }
1660
1661 Ok(1)
1662}
1663
1664fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
1672 out.fill(0);
1673 let n = crate::object::chunk_id(&mut out[..], source);
1674 if n < out.len() {
1675 out[n] = 0;
1676 }
1677}
1678
1679fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
1683 crate::func::get_local_name(p, n, pc)
1684}
1685
1686fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
1688 get_local_name(&cl.proto, n, pc)
1689}
1690
1691fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
1705 match state.get_at(ci.func) {
1706 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
1707 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
1708 }
1709}
1710
1711