1#[allow(unused_imports)] use crate::prelude::*;
11use crate::state::{
12 CallInfo, GcRef, LuaClosure, LuaClosureLua, LuaProto, LuaState, LuaTable, LuaValue,
13 UpVal, CIST_C, 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;
46const LUA_MASKCALL: u8 = 1 << 0;
47
48const LUA_HOOKLINE: i32 = 2;
49const LUA_HOOKCOUNT: i32 = 3;
50
51const LUA_YIELD_STATUS: i32 = 1;
53
54const LUA_ENV: &[u8] = b"_ENV";
56
57fn runtime_bytes(msg: Vec<u8>) -> LuaError {
64 LuaError::Runtime(lua_types::LuaValue::Str(lua_types::GcRef::new(
65 lua_types::LuaString::from_bytes(msg),
66 )))
67}
68
69pub(crate) fn prefixed_runtime_pub(state: &LuaState, msg: Vec<u8>) -> LuaError {
77 prefixed_runtime(state, msg)
78}
79
80fn prefixed_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
81 let ci_idx = state.current_ci_idx();
82 let ci = state.get_ci(ci_idx).clone();
83 if !ci.is_lua() {
84 return runtime_bytes(msg);
85 }
86 let proto = ci_lua_proto(&ci, state);
87 let src = proto.source_string();
88 let line = get_current_line(&ci, state);
89 let prefixed = add_info(
90 None,
91 &msg,
92 src.map(|s| &**s),
93 line,
94 );
95 runtime_bytes(prefixed)
96}
97
98pub fn c_api_runtime(state: &LuaState, msg: Vec<u8>) -> LuaError {
99 let ci_idx = state.current_ci_idx();
100 if let Some(parent_idx) = state.prev_ci(ci_idx) {
101 let parent_ci = state.get_ci(parent_idx).clone();
102 if parent_ci.is_lua() {
103 let proto = ci_lua_proto(&parent_ci, state);
104 let src = proto.source_string();
105 let line = get_current_line(&parent_ci, state);
106 let prefixed = add_info(None, &msg, src.map(|s| &**s), line);
107 return runtime_bytes(prefixed);
108 }
109 }
110 runtime_bytes(msg)
111}
112
113#[allow(dead_code)]
123fn find_func_in_table(table: &LuaTable, target: &LuaValue, prefix: &[u8], depth: u8) -> Option<Vec<u8>> {
124 let mut key = LuaValue::Nil;
125 loop {
126 let (k, v) = match table.next_pair(&key) {
127 Some(pair) => pair,
128 None => break,
129 };
130 if !matches!(v, LuaValue::Nil) {
131 let key_bytes: Option<Vec<u8>> = match &k {
132 LuaValue::Str(s) => Some(s.as_bytes().to_vec()),
133 _ => None,
134 };
135 if let Some(kb) = key_bytes {
136 if &v == target {
137 if prefix.is_empty() {
138 return Some(kb);
139 }
140 let mut result = prefix.to_vec();
141 result.push(b'.');
142 result.extend_from_slice(&kb);
143 return Some(result);
144 }
145 if depth > 0 {
146 if let LuaValue::Table(sub) = &v {
147 let new_prefix = if prefix.is_empty() {
148 kb.clone()
149 } else {
150 let mut p = prefix.to_vec();
151 p.push(b'.');
152 p.extend_from_slice(&kb);
153 p
154 };
155 if let Some(name) = find_func_in_table(&**sub, target, &new_prefix, depth - 1) {
156 return Some(name);
157 }
158 }
159 }
160 }
161 }
162 key = k;
163 }
164 None
165}
166
167#[allow(dead_code)]
175fn find_func_name_in_globals(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
176 let globals = state.global().globals.clone();
177 if let LuaValue::Table(globals_table) = globals {
178 find_func_in_table(&*globals_table, func_val, b"", 1)
179 } else {
180 None
181 }
182}
183
184fn find_func_name_in_loaded(state: &LuaState, func_val: &LuaValue) -> Option<Vec<u8>> {
191 let registry = state.global().l_registry.clone();
192 let loaded = match registry {
193 LuaValue::Table(ref reg_table) => reg_table.get_str_bytes(b"_LOADED"),
194 _ => return None,
195 };
196 let loaded_table = match loaded {
197 LuaValue::Table(t) => t,
198 _ => return None,
199 };
200 find_func_in_table(&*loaded_table, func_val, b"", 1)
201}
202
203pub fn arg_error_impl(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> LuaError {
207 let mut ar = LuaDebug::default();
208 if !get_stack(state, 0, &mut ar) {
209 let msg = format!("bad argument #{} ({})", arg, String::from_utf8_lossy(extramsg));
210 return c_api_runtime(state, msg.into_bytes());
211 }
212 get_info(state, b"n", &mut ar);
213 if ar.namewhat.as_deref() == Some(b"method") {
214 arg -= 1;
215 if arg == 0 {
216 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
217 let msg = format!(
218 "calling '{}' on bad self ({})",
219 String::from_utf8_lossy(&name),
220 String::from_utf8_lossy(extramsg)
221 );
222 return c_api_runtime(state, msg.into_bytes());
223 }
224 }
225 let fname = ar.name.clone().or_else(|| {
226 let ci_idx = ar.i_ci?;
227 let func_slot = state.get_ci(ci_idx).func;
228 let func_val = state.get_at(func_slot).clone();
229 let found = find_func_name_in_loaded(state, &func_val)?;
230 if found.starts_with(b"_G.") {
231 Some(found[3..].to_vec())
232 } else {
233 Some(found)
234 }
235 }).unwrap_or_else(|| b"?".to_vec());
236 let msg = format!(
237 "bad argument #{} to '{}' ({})",
238 arg,
239 String::from_utf8_lossy(&fname),
240 String::from_utf8_lossy(extramsg)
241 );
242 c_api_runtime(state, msg.into_bytes())
243}
244
245fn runtime_from_top(state: &mut crate::state::LuaState) -> LuaError {
250 let v = state.pop();
251 LuaError::Runtime(v)
252}
253
254pub struct LuaDebug {
265 pub event: i32,
266 pub name: Option<Vec<u8>>,
267 pub namewhat: Option<&'static [u8]>,
268 pub what: Option<&'static [u8]>,
269 pub source: Option<Vec<u8>>,
270 pub srclen: usize,
271 pub currentline: i32,
272 pub linedefined: i32,
273 pub lastlinedefined: i32,
274 pub nups: u8,
275 pub nparams: u8,
276 pub isvararg: bool,
277 pub istailcall: bool,
278 pub ftransfer: u16,
279 pub ntransfer: u16,
280 pub short_src: [u8; LUA_IDSIZE],
281 pub i_ci: Option<CallInfoIdx>,
283}
284
285impl Default for LuaDebug {
286 fn default() -> Self {
287 LuaDebug {
288 event: 0,
289 name: None,
290 namewhat: None,
291 what: None,
292 source: None,
293 srclen: 0,
294 currentline: -1,
295 linedefined: -1,
296 lastlinedefined: -1,
297 nups: 0,
298 nparams: 0,
299 isvararg: false,
300 istailcall: false,
301 ftransfer: 0,
302 ntransfer: 0,
303 short_src: [0u8; LUA_IDSIZE],
304 i_ci: None,
305 }
306 }
307}
308
309#[inline]
313fn is_lua_closure(cl: Option<&LuaClosure>) -> bool {
314 matches!(cl, Some(LuaClosure::Lua(_)))
315}
316
317fn current_pc(ci: &CallInfo) -> i32 {
332 debug_assert!(ci.is_lua());
333 ci.saved_pc().saturating_sub(1) as i32
336}
337
338fn get_baseline(f: &LuaProto, pc: i32, basepc: &mut i32) -> i32 {
346 if f.abslineinfo.is_empty() || pc < f.abslineinfo[0].pc {
347 *basepc = -1;
348 return f.linedefined;
349 }
350 let mut i = (pc as u32 / MAX_IWTH_ABS as u32).saturating_sub(1) as usize;
352 debug_assert!(
353 i < f.abslineinfo.len() && f.abslineinfo[i].pc <= pc,
354 "getbaseline: estimate is not a lower bound"
355 );
356 while i + 1 < f.abslineinfo.len() && pc >= f.abslineinfo[i + 1].pc {
357 i += 1;
358 }
359 *basepc = f.abslineinfo[i].pc;
360 f.abslineinfo[i].line
361}
362
363pub(crate) fn get_func_line(f: &LuaProto, pc: i32) -> i32 {
367 if f.lineinfo.is_empty() {
368 return -1;
369 }
370 let mut basepc: i32 = 0;
371 let mut baseline = get_baseline(f, pc, &mut basepc);
372 while basepc < pc {
375 basepc += 1;
376 debug_assert!(
377 f.lineinfo[basepc as usize] != ABS_LINE_INFO,
378 "get_func_line: hit ABSLINEINFO in incremental walk"
379 );
380 baseline += f.lineinfo[basepc as usize] as i32;
381 }
382 baseline
383}
384
385fn get_current_line(ci: &CallInfo, state: &LuaState) -> i32 {
388 let proto = ci_lua_proto(ci, state);
389 get_func_line(&proto, current_pc(ci))
390}
391
392fn set_traps(state: &mut LuaState) {
401 for ci in state.call_stack_mut().iter_mut() {
405 if ci.is_lua() {
406 ci.set_trap(true);
407 }
408 }
409}
410
411pub fn set_hook(
414 state: &mut LuaState,
415 func: Option<Box<dyn FnMut(&mut LuaState, &LuaDebug)>>,
416 mask: i32,
417 count: i32,
418) {
419 let (func, mask) = if func.is_none() || mask == 0 {
420 (None, 0i32)
421 } else {
422 (func, mask)
423 };
424 state.set_hook(func);
425 state.set_base_hook_count(count);
426 state.reset_hook_count();
428 state.set_hook_mask(mask as u8);
430 if mask != 0 {
431 set_traps(state);
432 }
433}
434
435pub fn get_hook_installed(state: &LuaState) -> bool {
442 state.hook().is_some()
443}
444
445pub fn get_hook_mask(state: &LuaState) -> i32 {
448 state.hook_mask() as i32
449}
450
451pub fn get_hook_count(state: &LuaState) -> i32 {
454 state.base_hook_count()
455}
456
457pub fn get_stack(state: &LuaState, level: i32, ar: &mut LuaDebug) -> bool {
464 if level < 0 {
465 return false;
466 }
467 let mut remaining = level;
468 let mut ci_idx = state.current_ci_idx();
469 loop {
470 if remaining == 0 {
471 break;
472 }
473 match state.prev_ci(ci_idx) {
474 Some(prev) => {
475 ci_idx = prev;
476 remaining -= 1;
477 }
478 None => {
479 return false;
480 }
481 }
482 }
483 if !state.is_base_ci(ci_idx) {
484 ar.i_ci = Some(ci_idx);
485 true
486 } else {
487 false
488 }
489}
490
491fn upval_name(p: &LuaProto, uv: usize) -> &[u8] {
496 debug_assert!(uv < p.upvalues.len(), "upval_name: index out of range");
499 p.upvalues[uv].name.as_ref().map_or(b"?" as &[u8], |s| s.as_bytes())
502}
503
504fn find_vararg(state: &LuaState, ci: &CallInfo, n: i32) -> Option<(StackIdx, &'static [u8])> {
511 let proto = ci_lua_proto(ci, state);
512 if proto.is_vararg {
513 let nextra = ci.nextra_args();
514 if n >= -(nextra as i32) {
515 let pos = ci.func - (nextra + n + 1);
518 return Some((pos, b"(vararg)" as &[u8]));
519 }
520 }
521 None
522}
523
524pub(crate) fn find_local(
537 state: &LuaState,
538 ci_idx: CallInfoIdx,
539 n: i32,
540 pos: Option<&mut StackIdx>,
541) -> Option<Vec<u8>> {
542 let ci = state.get_ci(ci_idx);
543 let base = ci.func + 1;
544 let mut name: Option<Vec<u8>> = None;
545
546 if ci.is_lua() {
547 if n < 0 {
548 if let Some((vpos, vname)) = find_vararg(state, ci, n) {
549 if let Some(out_pos) = pos {
550 *out_pos = vpos;
551 }
552 return Some(vname.to_vec());
553 }
554 return None;
555 } else {
556 let proto = ci_lua_proto(ci, state);
557 let pc = current_pc(ci);
558 name = crate::func::get_local_name(&proto, n, pc).map(|s| s.to_vec());
559 }
560 }
561
562 if name.is_none() {
563 let limit: u32 = if ci_idx == state.current_ci_idx() {
564 state.top_idx().0
565 } else {
566 ci.next
567 .map(|next| state.get_ci(next).func.0)
568 .unwrap_or_else(|| state.top_idx().0)
569 };
570 if n > 0 && limit.saturating_sub(base.0) >= n as u32 {
571 name = Some(if ci.is_lua() { b"(temporary)".to_vec() } else { b"(C temporary)".to_vec() });
572 } else {
573 return None;
574 }
575 }
576
577 if let Some(out_pos) = pos {
578 *out_pos = base + (n - 1);
579 }
580 name
581}
582
583pub fn get_local(state: &mut LuaState, ar: Option<&LuaDebug>, n: i32) -> Option<Vec<u8>> {
588 if ar.is_none() {
589 let top_val = state.peek_top();
591 if !matches!(top_val, LuaValue::Function(LuaClosure::Lua(_))) {
592 return None;
593 }
594 let name_owned: Option<Vec<u8>> = {
597 let cl = match top_val {
598 LuaValue::Function(LuaClosure::Lua(ref cl)) => cl.clone(),
599 _ => unreachable!(),
600 };
601 get_local_name_from_closure(&cl, n, 0).map(|s| s.to_vec())
603 };
604 return name_owned;
605 }
606
607 let ar = ar.unwrap();
608 let ci_idx = ar.i_ci?;
609 let mut pos = StackIdx(0);
610 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
613
614 if name_owned.is_some() {
615 let val = state.get_at(pos).clone();
616 state.push(val);
617 }
618 name_owned
619}
620
621pub fn set_local(state: &mut LuaState, ar: &LuaDebug, n: i32) -> Option<Vec<u8>> {
625 let ci_idx = ar.i_ci?;
626 let mut pos = StackIdx(0);
627 let name_owned: Option<Vec<u8>> = find_local(state, ci_idx, n, Some(&mut pos));
629 if name_owned.is_some() {
630 let val = state.get_at(state.top_idx() - 1).clone();
631 state.set_at(pos, val);
632 state.pop_n(1);
633 }
634 name_owned
635}
636
637fn func_info(ar: &mut LuaDebug, cl: Option<&LuaClosure>) {
642 if !is_lua_closure(cl) {
643 ar.source = Some(b"=[C]".to_vec());
645 ar.srclen = b"=[C]".len();
646 ar.linedefined = -1;
647 ar.lastlinedefined = -1;
648 ar.what = Some(b"C");
649 } else {
650 let lua_cl = match cl {
651 Some(LuaClosure::Lua(cl)) => cl,
652 _ => unreachable!(),
653 };
654 let proto: &LuaProto = &lua_cl.proto;
656 if let Some(src) = proto.source_string() {
658 ar.source = Some(src.as_bytes().to_vec());
659 ar.srclen = src.as_bytes().len();
660 } else {
661 ar.source = Some(b"=?".to_vec());
662 ar.srclen = b"=?".len();
663 }
664 ar.linedefined = proto.linedefined;
665 ar.lastlinedefined = proto.lastlinedefined;
666 ar.what = Some(if ar.linedefined == 0 { b"main" } else { b"Lua" });
667 }
668 chunk_id(&mut ar.short_src, ar.source.as_deref().unwrap_or(b"?"), ar.srclen);
670}
671
672fn next_line(p: &LuaProto, currentline: i32, pc: usize) -> i32 {
676 if p.lineinfo.get(pc).copied() != Some(ABS_LINE_INFO) {
678 currentline + p.lineinfo[pc] as i32
679 } else {
680 get_func_line(p, pc as i32)
681 }
682}
683
684fn collect_valid_lines(state: &mut LuaState, cl: Option<&LuaClosure>) -> Result<(), LuaError> {
688 if !is_lua_closure(cl) {
689 state.push(LuaValue::Nil);
691 return Ok(());
692 }
693 let lua_cl = match cl {
694 Some(LuaClosure::Lua(cl)) => cl.clone(),
695 _ => unreachable!(),
696 };
697 let proto: GcRef<LuaProto> = lua_cl.proto.clone();
699 let p: &LuaProto = &proto;
700
701 let mut currentline = p.linedefined;
702
703 let t = state.new_table();
705 state.push(LuaValue::Table(t.clone()));
707
708 if !p.lineinfo.is_empty() {
709 let v = LuaValue::Bool(true);
711
712 let start_i = if !p.is_vararg {
713 0usize
714 } else {
715 debug_assert!(
717 p.code.first().map(|i| i.is_vararg_prep()).unwrap_or(false),
718 "collect_valid_lines: first instruction of vararg should be OP_VARARGPREP"
719 );
720 currentline = next_line(p, currentline, 0);
721 1usize
722 };
723
724 for i in start_i..p.lineinfo.len() {
726 currentline = next_line(p, currentline, i);
727 t.raw_set_int(state, currentline as i64, v.clone())?;
729 }
730 }
731 Ok(())
732}
733
734fn get_func_name<'a>(
740 state: &'a LuaState,
741 ci: Option<&CallInfo>,
742 name: &mut Option<Vec<u8>>,
743) -> Option<&'static [u8]> {
744 let ci = ci?;
747 if ci.callstatus & CIST_TAIL != 0 {
748 return None;
749 }
750 let prev_idx = ci.previous?;
753 let prev_ci = state.get_ci(prev_idx).clone();
754 funcname_from_call(state, &prev_ci, name)
755}
756
757fn aux_get_info(
760 state: &LuaState,
761 what: &[u8],
762 ar: &mut LuaDebug,
763 cl: Option<&LuaClosure>,
764 ci: Option<&CallInfo>,
765) -> bool {
766 let mut status = true;
767 for &ch in what {
768 match ch {
769 b'S' => {
770 func_info(ar, cl);
771 }
772 b'l' => {
773 ar.currentline = match ci {
774 Some(ci) if ci.is_lua() => get_current_line(ci, state),
775 _ => -1,
776 };
777 }
778 b'u' => {
779 ar.nups = cl.map_or(0, |c| c.nupvalues() as u8);
780 match cl {
781 Some(LuaClosure::Lua(lua_cl)) => {
782 ar.isvararg = lua_cl.proto.is_vararg;
784 ar.nparams = lua_cl.proto.numparams;
785 }
786 _ => {
787 ar.isvararg = true;
788 ar.nparams = 0;
789 }
790 }
791 }
792 b't' => {
793 ar.istailcall = ci.map_or(false, |ci| ci.callstatus & CIST_TAIL != 0);
794 }
795 b'n' => {
796 let mut name: Option<Vec<u8>> = None;
797 ar.namewhat = get_func_name(state, ci, &mut name);
798 if ar.namewhat.is_none() {
799 ar.namewhat = Some(b"");
800 ar.name = None;
801 } else {
802 ar.name = name;
803 }
804 }
805 b'r' => match ci {
807 Some(ci) if ci.callstatus & CIST_TRAN != 0 => {
808 ar.ftransfer = ci.transfer_ftransfer();
810 ar.ntransfer = ci.transfer_ntransfer();
811 }
812 _ => {
813 ar.ftransfer = 0;
814 ar.ntransfer = 0;
815 }
816 },
817 b'L' | b'f' => {}
818 _ => {
819 status = false;
820 }
821 }
822 }
823 status
824}
825
826pub fn get_info(state: &mut LuaState, what: &[u8], ar: &mut LuaDebug) -> bool {
829 let (cl, ci_idx, func_val, what) = if what.first() == Some(&b'>') {
830 let func_val = state.peek_at(state.top_idx() - 1).clone();
831 state.pop_n(1);
832 debug_assert!(
833 matches!(func_val, LuaValue::Function(_)),
834 "get_info: function expected"
835 );
836 let cl = match &func_val {
837 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => {
838 Some(match &func_val {
839 LuaValue::Function(c) => c.clone(),
840 _ => unreachable!(),
841 })
842 }
843 _ => None,
844 };
845 (cl, None, func_val, &what[1..])
846 } else {
847 let ci_idx = match ar.i_ci { Some(i) => i, None => return false };
848 let func_val = state.get_at(state.get_ci(ci_idx).func).clone();
849 debug_assert!(
850 matches!(func_val, LuaValue::Function(_)),
851 "get_info: non-function at ci->func"
852 );
853 let cl = match &func_val {
854 LuaValue::Function(LuaClosure::Lua(_) | LuaClosure::C(_)) => {
855 Some(match &func_val {
856 LuaValue::Function(c) => c.clone(),
857 _ => unreachable!(),
858 })
859 }
860 _ => None,
861 };
862 (cl, Some(ci_idx), func_val, what)
863 };
864
865 let ci = ci_idx.and_then(|idx| Some(state.get_ci(idx).clone()));
866 let status = aux_get_info(state, what, ar, cl.as_ref(), ci.as_ref());
867
868 if what.contains(&b'f') {
869 state.push(func_val);
870 }
871 if what.contains(&b'L') {
872 let _ = collect_valid_lines(state, cl.as_ref());
874 }
875 status
876}
877
878#[inline]
884fn filter_pc(pc: i32, jmptarget: i32) -> i32 {
885 if pc < jmptarget { -1 } else { pc }
886}
887
888fn find_set_reg(p: &LuaProto, lastpc: i32, reg: i32) -> i32 {
892 let mut setreg: i32 = -1;
893 let mut jmptarget: i32 = 0;
894
895 let effective_lastpc = if p.code.get(lastpc as usize).map_or(false, |i| i.is_mm_mode()) {
898 lastpc - 1
899 } else {
900 lastpc
901 };
902
903 for pc in 0..effective_lastpc {
904 let instr = p.code[pc as usize];
905 let op = instr.opcode();
906 let a = instr.arg_a() as i32;
907
908 let change = match op {
909 OpCode::LoadNil => {
910 let b = instr.arg_b() as i32;
911 a <= reg && reg <= a + b
912 }
913 OpCode::TForCall => reg >= a + 2,
914 OpCode::Call | OpCode::TailCall => reg >= a,
915 OpCode::Jmp => {
916 let b = instr.arg_s_j();
917 let dest = pc + 1 + b;
918 if dest <= effective_lastpc && dest > jmptarget {
919 jmptarget = dest;
920 }
921 false
922 }
923 _ => {
924 instr.test_a_mode() && reg == a
927 }
928 };
929
930 if change {
931 setreg = filter_pc(pc, jmptarget);
932 }
933 }
934 setreg
935}
936
937fn kname<'a>(p: &'a LuaProto, index: usize, name: &mut &'a [u8]) -> Option<&'static [u8]> {
942 match p.k.get(index) {
945 Some(LuaValue::Str(s)) => {
946 *name = s.as_bytes();
948 Some(b"constant")
949 }
950 _ => {
951 *name = b"?";
952 None
953 }
954 }
955}
956
957fn basic_get_obj_name<'a>(
961 p: &'a LuaProto,
962 ppc: &mut i32,
963 reg: i32,
964 name: &mut &'a [u8],
965) -> Option<&'static [u8]> {
966 let pc = *ppc;
967 if let Some(local_name) = get_local_name(p, reg + 1, pc) {
969 *name = local_name;
970 return Some(b"local");
971 }
972
973 *ppc = find_set_reg(p, pc, reg);
974 let pc = *ppc;
975
976 if pc == -1 {
977 return None;
978 }
979
980 let instr = p.code[pc as usize];
981 let op = instr.opcode();
982 match op {
983 OpCode::Move => {
984 let b = instr.arg_b() as i32;
985 if b < instr.arg_a() as i32 {
986 return basic_get_obj_name(p, ppc, b, name);
987 }
988 }
989 OpCode::GetUpVal => {
990 *name = upval_name(p, instr.arg_b() as usize);
991 return Some(b"upvalue");
992 }
993 OpCode::LoadK => {
994 return kname(p, instr.arg_bx() as usize, name);
995 }
996 OpCode::LoadKx => {
997 let next = p.code[(pc + 1) as usize];
998 return kname(p, next.arg_ax() as usize, name);
999 }
1000 _ => {}
1001 }
1002 None
1003}
1004
1005fn rname<'a>(p: &'a LuaProto, pc: i32, c: i32, name: &mut &'a [u8]) {
1009 let mut pc = pc;
1010 let what = basic_get_obj_name(p, &mut pc, c, name);
1012 if !matches!(what, Some(kind) if kind.first() == Some(&b'c')) {
1013 *name = b"?";
1014 }
1015}
1016
1017fn rkname<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, name: &mut &'a [u8]) {
1020 let c = instr.arg_c() as i32;
1021 if instr.arg_k() != 0 {
1023 kname(p, c as usize, name);
1024 } else {
1025 rname(p, pc, c, name);
1026 }
1027}
1028
1029fn is_env<'a>(p: &'a LuaProto, pc: i32, instr: Instruction, isup: bool) -> &'static [u8] {
1033 let t = instr.arg_b() as usize;
1034 let mut name: &[u8] = b"?";
1035 if isup {
1036 name = upval_name(p, t);
1037 } else {
1038 let mut pc = pc;
1039 basic_get_obj_name(p, &mut pc, t as i32, &mut name);
1040 }
1041 if name == LUA_ENV { b"global" } else { b"field" }
1042}
1043
1044fn get_obj_name<'a>(
1048 p: &'a LuaProto,
1049 lastpc: i32,
1050 reg: i32,
1051 name: &mut &'a [u8],
1052) -> Option<&'static [u8]> {
1053 let mut lastpc = lastpc;
1054 let kind = basic_get_obj_name(p, &mut lastpc, reg, name);
1055 if kind.is_some() {
1056 return kind;
1057 }
1058
1059 if lastpc == -1 {
1060 return None;
1061 }
1062
1063 let instr = p.code[lastpc as usize];
1064 let op = instr.opcode();
1065 match op {
1066 OpCode::GetTabUp => {
1067 let k = instr.arg_c() as usize;
1068 kname(p, k, name);
1069 Some(is_env(p, lastpc, instr, true))
1070 }
1071 OpCode::GetTable => {
1072 let k = instr.arg_c() as i32;
1073 rname(p, lastpc, k, name);
1074 Some(is_env(p, lastpc, instr, false))
1075 }
1076 OpCode::GetI => {
1077 *name = b"integer index";
1078 Some(b"field")
1079 }
1080 OpCode::GetField => {
1081 let k = instr.arg_c() as usize;
1082 kname(p, k, name);
1083 Some(is_env(p, lastpc, instr, false))
1084 }
1085 OpCode::Self_ => {
1086 rkname(p, lastpc, instr, name);
1087 Some(b"method")
1088 }
1089 _ => None,
1090 }
1091}
1092
1093fn funcname_from_code<'a>(
1100 state: &LuaState,
1101 p: &'a LuaProto,
1102 pc: i32,
1103 name: &mut Option<Vec<u8>>,
1104) -> Option<&'static [u8]> {
1105 let instr = p.code[pc as usize];
1106 let op = instr.opcode();
1107
1108 match op {
1109 OpCode::Call | OpCode::TailCall => {
1110 let mut name_bytes: &[u8] = b"?";
1111 let kind = get_obj_name(p, pc, instr.arg_a() as i32, &mut name_bytes);
1112 *name = Some(name_bytes.to_vec());
1113 kind
1114 }
1115 OpCode::TForCall => {
1116 *name = Some(b"for iterator".to_vec());
1117 Some(b"for iterator")
1118 }
1119 OpCode::Self_ | OpCode::GetTabUp | OpCode::GetTable | OpCode::GetI | OpCode::GetField => {
1121 get_tm_name(state, TagMethod::Index, name)
1122 }
1123 OpCode::SetTabUp | OpCode::SetTable | OpCode::SetI | OpCode::SetField => {
1124 get_tm_name(state, TagMethod::NewIndex, name)
1125 }
1126 OpCode::MmBin | OpCode::MmBinI | OpCode::MmBinK => {
1127 let tm_idx = instr.arg_c() as u8;
1130 let tm = TagMethod::from_u8(tm_idx);
1131 get_tm_name(state, tm, name)
1132 }
1133 OpCode::Unm => get_tm_name(state, TagMethod::Unm, name),
1134 OpCode::BNot => get_tm_name(state, TagMethod::BNot, name),
1135 OpCode::Len => get_tm_name(state, TagMethod::Len, name),
1136 OpCode::Concat => get_tm_name(state, TagMethod::Concat, name),
1137 OpCode::Eq => get_tm_name(state, TagMethod::Eq, name),
1138 OpCode::Lt | OpCode::LtI | OpCode::GtI => get_tm_name(state, TagMethod::Lt, name),
1139 OpCode::Le | OpCode::LeI | OpCode::GeI => get_tm_name(state, TagMethod::Le, name),
1140 OpCode::Close | OpCode::Return => get_tm_name(state, TagMethod::Close, name),
1141 _ => None,
1142 }
1143}
1144
1145fn get_tm_name(
1151 state: &LuaState,
1152 tm: TagMethod,
1153 name: &mut Option<Vec<u8>>,
1154) -> Option<&'static [u8]> {
1155 let raw_bytes: Vec<u8> = state.global()
1159 .tm_name(tm)
1160 .map(|s| s.as_bytes().to_vec())
1161 .unwrap_or_default();
1162 let stripped = raw_bytes
1163 .strip_prefix(b"__")
1164 .unwrap_or(&raw_bytes)
1165 .to_vec();
1166 *name = Some(stripped);
1167 Some(b"metamethod")
1168}
1169
1170fn funcname_from_call<'a>(
1173 state: &'a LuaState,
1174 ci: &CallInfo,
1175 name: &mut Option<Vec<u8>>,
1176) -> Option<&'static [u8]> {
1177 if ci.callstatus & CIST_HOOKED != 0 {
1178 *name = Some(b"?".to_vec());
1179 return Some(b"hook");
1180 }
1181 if ci.callstatus & CIST_FIN != 0 {
1182 *name = Some(b"__gc".to_vec());
1183 return Some(b"metamethod");
1184 }
1185 if ci.is_lua() {
1186 let proto = ci_lua_proto(ci, state);
1187 return funcname_from_code(state, &proto, current_pc(ci), name);
1188 }
1189 None
1190}
1191
1192fn in_stack(ci: &CallInfo, val_idx: StackIdx, state: &LuaState) -> i32 {
1203 let base = StackIdx(ci.func.0 + 1);
1204 let ci_top = ci.top;
1207 let mut pos = 0i32;
1208 let mut cur = base;
1209 while cur.0 < ci_top.0 {
1210 if cur == val_idx {
1211 return pos;
1212 }
1213 cur = StackIdx(cur.0 + 1);
1214 pos += 1;
1215 }
1216 -1
1217}
1218
1219fn get_upval_name<'a>(
1228 ci: &CallInfo,
1229 val_idx: StackIdx,
1230 name: &mut &'a [u8],
1231 state: &'a LuaState,
1232) -> Option<&'static [u8]> {
1233 let proto = ci_lua_proto(ci, state);
1234 let lua_cl = match state.get_at(ci.func) {
1237 LuaValue::Function(LuaClosure::Lua(cl)) => cl.clone(),
1238 _ => return None,
1239 };
1240 for (i, upval_slot) in lua_cl.upvals.iter().enumerate() {
1241 let upval = upval_slot.get();
1242 let state = upval.slot().clone();
1243 if let lua_types::UpValState::Open { idx, .. } = state {
1244 if idx == val_idx {
1245 let _ = upval_name(&proto, i);
1248 *name = b"upvalue";
1249 return Some(b"upvalue");
1250 }
1251 }
1252 }
1253 None
1254}
1255
1256fn format_var_info(kind: Option<&[u8]>, name: Option<&[u8]>) -> Vec<u8> {
1261 match (kind, name) {
1262 (Some(k), Some(n)) => {
1263 let mut out = Vec::with_capacity(4 + k.len() + n.len());
1264 out.extend_from_slice(b" (");
1265 out.extend_from_slice(k);
1266 out.extend_from_slice(b" '");
1267 out.extend_from_slice(n);
1268 out.extend_from_slice(b"')");
1269 out
1270 }
1271 _ => Vec::new(),
1272 }
1273}
1274
1275fn var_info(state: &LuaState, val_idx: StackIdx) -> Vec<u8> {
1279 let ci_idx = state.current_ci_idx();
1280 let ci = state.get_ci(ci_idx).clone();
1281 let mut kind: Option<&[u8]> = None;
1282 let mut name_owned: Vec<u8> = b"?".to_vec();
1283
1284 if ci.is_lua() {
1285 let mut up_name: &[u8] = b"?";
1286 kind = get_upval_name(&ci, val_idx, &mut up_name, state);
1287 if kind.is_some() {
1288 name_owned = up_name.to_vec();
1289 } else {
1290 let reg = in_stack(&ci, val_idx, state);
1291 if reg >= 0 {
1292 let proto = ci_lua_proto(&ci, state);
1293 let mut nref: &[u8] = b"?";
1294 let pc = current_pc(&ci);
1295 let k = get_obj_name(&proto, pc, reg, &mut nref);
1296 kind = k;
1297 if kind.is_some() {
1298 name_owned = nref.to_vec();
1299 }
1300 }
1301 }
1302 }
1303 format_var_info(kind, if kind.is_some() { Some(&name_owned) } else { None })
1304}
1305
1306fn typeerror_inner(
1311 state: &LuaState,
1312 val: &LuaValue,
1313 op: &[u8],
1314 extra: &[u8],
1315) -> LuaError {
1316 let t = state.obj_type_name(val);
1317 let mut msg = Vec::new();
1318 msg.extend_from_slice(b"attempt to ");
1319 msg.extend_from_slice(op);
1320 msg.extend_from_slice(b" a ");
1321 msg.extend_from_slice(&t);
1322 msg.extend_from_slice(b" value");
1323 msg.extend_from_slice(extra);
1324 prefixed_runtime(state, msg)
1325}
1326
1327pub(crate) fn type_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx, op: &[u8]) -> LuaError {
1331 let extra = var_info(state, val_idx);
1332 typeerror_inner(state, val, op, &extra)
1333}
1334
1335pub(crate) fn type_error_with_hint(
1341 state: &LuaState,
1342 val: &LuaValue,
1343 op: &[u8],
1344 kind: &[u8],
1345 name: &[u8],
1346) -> LuaError {
1347 let extra = format_var_info(Some(kind), Some(name));
1348 let t = obj_type_name_static(val);
1349 let mut msg = Vec::new();
1350 msg.extend_from_slice(b"attempt to ");
1351 msg.extend_from_slice(op);
1352 msg.extend_from_slice(b" a ");
1353 msg.extend_from_slice(t);
1354 msg.extend_from_slice(b" value");
1355 msg.extend_from_slice(&extra);
1356 prefixed_runtime(state, msg)
1357}
1358
1359fn obj_type_name_static(val: &LuaValue) -> &'static [u8] {
1362 match val {
1363 LuaValue::Nil => b"nil",
1364 LuaValue::Bool(_) => b"boolean",
1365 LuaValue::Int(_) | LuaValue::Float(_) => b"number",
1366 LuaValue::Str(_) => b"string",
1367 LuaValue::Table(_) => b"table",
1368 LuaValue::Function(_) => b"function",
1369 LuaValue::UserData(_) => b"userdata",
1370 LuaValue::LightUserData(_) => b"light userdata",
1371 LuaValue::Thread(_) => b"thread",
1372 }
1373}
1374
1375pub(crate) fn call_error(state: &LuaState, val: &LuaValue, val_idx: StackIdx) -> LuaError {
1379 let ci_idx = state.current_ci_idx();
1380 let ci = state.get_ci(ci_idx).clone();
1381 let mut name: Option<Vec<u8>> = None;
1382 let kind = funcname_from_call(state, &ci, &mut name);
1383 let extra = if kind.is_some() {
1384 format_var_info(kind, name.as_deref())
1385 } else {
1386 var_info(state, val_idx)
1387 };
1388 typeerror_inner(state, val, b"call", &extra)
1389}
1390
1391pub(crate) fn for_error(state: &mut LuaState, val: &LuaValue, what: &[u8]) -> LuaError {
1394 let t = crate::tagmethods::obj_type_name(state, val)
1395 .unwrap_or_else(|_| crate::tagmethods::type_name(val.base_type()).to_vec());
1396 let mut msg = Vec::new();
1397 msg.extend_from_slice(b"bad 'for' ");
1398 msg.extend_from_slice(what);
1399 msg.extend_from_slice(b" (number expected, got ");
1400 msg.extend_from_slice(&t);
1401 msg.push(b')');
1402 prefixed_runtime(state, msg)
1403}
1404
1405pub(crate) fn concat_error(
1408 state: &LuaState,
1409 p1: &LuaValue,
1410 p1_idx: StackIdx,
1411 p2: &LuaValue,
1412 p2_idx: StackIdx,
1413) -> LuaError {
1414 let (bad_val, bad_idx) = if matches!(p1, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_)) {
1417 (p2, p2_idx)
1418 } else {
1419 (p1, p1_idx)
1420 };
1421 type_error(state, bad_val, bad_idx, b"concatenate")
1422}
1423
1424pub(crate) fn op_int_error(
1428 state: &LuaState,
1429 p1: &LuaValue,
1430 p1_idx: StackIdx,
1431 p2: &LuaValue,
1432 p2_idx: StackIdx,
1433 msg: &[u8],
1434) -> LuaError {
1435 let (bad_val, bad_idx) = if !matches!(p1, LuaValue::Int(_) | LuaValue::Float(_)) {
1437 (p1, p1_idx)
1438 } else {
1439 (p2, p2_idx)
1440 };
1441 type_error(state, bad_val, bad_idx, msg)
1442}
1443
1444pub(crate) fn to_int_error(
1450 state: &LuaState,
1451 p1: &LuaValue,
1452 p1_idx: Option<StackIdx>,
1453 _p2: &LuaValue,
1454 p2_idx: Option<StackIdx>,
1455) -> LuaError {
1456 let bad_idx = if p1.to_integer_no_strconv().is_none() {
1457 p1_idx
1458 } else {
1459 p2_idx
1460 };
1461 let extra = match bad_idx {
1462 Some(idx) => var_info(state, idx),
1463 None => Vec::new(),
1464 };
1465 let mut msg = Vec::new();
1466 msg.extend_from_slice(b"number");
1467 msg.extend_from_slice(&extra);
1468 msg.extend_from_slice(b" has no integer representation");
1469 prefixed_runtime(state, msg)
1470}
1471
1472pub(crate) fn order_error(state: &LuaState, p1: &LuaValue, p2: &LuaValue) -> LuaError {
1475 let t1 = state.obj_type_name(p1);
1477 let t2 = state.obj_type_name(p2);
1478 let msg = if t1 == t2 {
1480 let mut m = Vec::new();
1481 m.extend_from_slice(b"attempt to compare two ");
1482 m.extend_from_slice(&t1);
1483 m.extend_from_slice(b" values");
1484 m
1485 } else {
1486 let mut m = Vec::new();
1487 m.extend_from_slice(b"attempt to compare ");
1488 m.extend_from_slice(&t1);
1489 m.extend_from_slice(b" with ");
1490 m.extend_from_slice(&t2);
1491 m
1492 };
1493 prefixed_runtime(state, msg)
1494}
1495
1496pub(crate) fn add_info(
1505 _state: Option<&mut LuaState>,
1506 msg: &[u8],
1507 src: Option<&LuaString>,
1508 line: i32,
1509) -> Vec<u8> {
1510 let mut buff = [0u8; LUA_IDSIZE];
1512 if let Some(src) = src {
1513 chunk_id(&mut buff, src.as_bytes(), src.len());
1516 } else {
1517 buff[0] = b'?';
1518 }
1519 let src_part = buff.iter().position(|&b| b == 0).map_or(&buff[..], |n| &buff[..n]);
1522 let mut out = Vec::with_capacity(src_part.len() + 12 + msg.len());
1523 out.extend_from_slice(src_part);
1524 out.push(b':');
1525 let line_str = line.to_string();
1527 out.extend_from_slice(line_str.as_bytes());
1528 out.extend_from_slice(b": ");
1529 out.extend_from_slice(msg);
1530 out
1531}
1532
1533pub(crate) fn error_msg(state: &mut LuaState) -> Result<(), LuaError> {
1537 if state.errfunc() != 0 {
1540 let errfunc_idx = StackIdx(state.errfunc() as u32);
1541 debug_assert!(
1542 matches!(state.get_at(errfunc_idx), LuaValue::Function(_)),
1543 "error_msg: error handler is not a function"
1544 );
1545 let arg = state.get_at(state.top_idx() - 1).clone();
1546 state.push(arg);
1547 let func = state.get_at(errfunc_idx).clone();
1548 state.set_at(state.top_idx() - 2, func);
1549 state.call_no_yield(state.top_idx() - 2, 1)?;
1552 }
1553 Err(runtime_from_top(state))
1556}
1557
1558pub(crate) fn run_error(state: &mut LuaState, msg: Vec<u8>) -> Result<(), LuaError> {
1562 state.gc().check_step();
1564
1565 let ci_idx = state.current_ci_idx();
1566 let ci = state.get_ci(ci_idx).clone();
1567
1568 let final_msg = if ci.is_lua() {
1569 let line = get_current_line(&ci, state);
1571 let proto = ci_lua_proto(&ci, state);
1572 let src = proto.source_string();
1573 add_info(Some(state), &msg, src.map(|s| &**s), line)
1574 } else {
1575 msg
1576 };
1577
1578 let str_val = state.new_string(&final_msg)?;
1581 state.push(LuaValue::Str(str_val));
1582 error_msg(state)
1583}
1584
1585fn changed_line(p: &LuaProto, oldpc: i32, newpc: i32) -> bool {
1590 if p.lineinfo.is_empty() {
1591 return false;
1592 }
1593
1594 if newpc - oldpc < MAX_IWTH_ABS / 2 {
1595 let mut delta: i32 = 0;
1596 let mut pc = oldpc;
1597 loop {
1598 pc += 1;
1599 if pc as usize >= p.lineinfo.len() {
1600 break;
1601 }
1602 let lineinfo = p.lineinfo[pc as usize];
1603 if lineinfo == ABS_LINE_INFO {
1604 break;
1605 }
1606 delta += lineinfo as i32;
1607 if pc == newpc {
1608 return delta != 0;
1609 }
1610 }
1611 }
1612 get_func_line(p, oldpc) != get_func_line(p, newpc)
1613}
1614
1615pub(crate) fn trace_call(state: &mut LuaState) -> Result<i32, LuaError> {
1621 let ci_idx = state.current_ci_idx();
1622 let ci = state.get_ci(ci_idx).clone();
1623 state.get_ci_mut(ci_idx).set_trap(true);
1624 let proto = ci_lua_proto(&ci, state);
1625
1626 if ci.saved_pc() == 0 {
1627 if proto.is_vararg {
1628 return Ok(0);
1629 } else if ci.callstatus & CIST_HOOKYIELD == 0 {
1630 state.hook_call(ci_idx)?;
1632 }
1633 }
1634 Ok(1)
1635}
1636
1637pub(crate) fn trace_exec(state: &mut LuaState, pc: u32) -> Result<i32, LuaError> {
1647 let ci_idx = state.current_ci_idx();
1648 let ci = state.get_ci(ci_idx).clone();
1649
1650 let mask = state.hook_mask();
1651
1652 if !state.allowhook {
1653 return Ok(1);
1654 }
1655
1656 if mask & (LUA_MASKLINE | LUA_MASKCOUNT) == 0 {
1657 state.get_ci_mut(ci_idx).set_trap(false);
1658 return Ok(0);
1659 }
1660
1661 let next_pc = pc + 1;
1662 state.get_ci_mut(ci_idx).set_saved_pc(next_pc);
1663
1664 let counthook = if mask & LUA_MASKCOUNT != 0 {
1665 let hc = state.hook_count() - 1;
1666 state.set_hook_count(hc);
1667 hc == 0
1668 } else {
1669 false
1670 };
1671
1672 if counthook {
1673 state.reset_hook_count();
1674 } else if mask & LUA_MASKLINE == 0 {
1675 return Ok(1);
1676 }
1677
1678 if ci.callstatus & CIST_HOOKYIELD != 0 {
1679 state.get_ci_mut(ci_idx).callstatus &= !CIST_HOOKYIELD;
1680 return Ok(1);
1681 }
1682
1683 if state.ci_lua_closure(ci_idx).is_none() {
1684 return Ok(1);
1685 }
1686
1687 let cur_instr = state.get_proto_instr(ci_idx, pc as u32);
1690 if !cur_instr.is_in_top() {
1691 let ci_top = state.get_ci(ci_idx).top;
1692 state.set_top(ci_top);
1693 }
1694
1695 if counthook {
1696 state.call_hook_event(LUA_HOOKCOUNT, -1)?;
1698 }
1699
1700 if mask & LUA_MASKLINE != 0 {
1701 let proto = ci_lua_proto(&ci, state);
1702 let oldpc = if state.old_pc() < proto.code.len() as u32 {
1703 state.old_pc() as i32
1704 } else {
1705 0
1706 };
1707 let npci = next_pc as i32 - 1;
1709
1710 if npci <= oldpc || changed_line(&proto, oldpc, npci) {
1711 let newline = get_func_line(&proto, npci);
1712 state.call_hook_event(LUA_HOOKLINE, newline)?;
1714 }
1715 state.set_old_pc(npci as u32);
1716 }
1717
1718 if state.status() == lua_types::status::LuaStatus::Yield {
1719 if counthook {
1720 state.set_hook_count(1);
1721 }
1722 state.get_ci_mut(ci_idx).callstatus |= CIST_HOOKYIELD;
1723 return Err(LuaError::Yield);
1725 }
1726
1727 Ok(1)
1728}
1729
1730fn chunk_id(out: &mut [u8; LUA_IDSIZE], source: &[u8], _srclen: usize) {
1738 out.fill(0);
1739 let n = crate::object::chunk_id(&mut out[..], source);
1740 if n < out.len() {
1741 out[n] = 0;
1742 }
1743}
1744
1745fn get_local_name(p: &LuaProto, n: i32, pc: i32) -> Option<&[u8]> {
1749 crate::func::get_local_name(p, n, pc)
1750}
1751
1752fn get_local_name_from_closure(cl: &LuaClosureLua, n: i32, pc: i32) -> Option<&[u8]> {
1754 get_local_name(&cl.proto, n, pc)
1755}
1756
1757fn ci_lua_proto(ci: &CallInfo, state: &LuaState) -> GcRef<LuaProto> {
1771 match state.get_at(ci.func) {
1772 LuaValue::Function(LuaClosure::Lua(cl)) => cl.proto.clone(),
1773 _ => panic!("ci_lua_proto: call frame does not hold a Lua closure"),
1774 }
1775}
1776
1777