1use crate::runtime::Value;
11use crate::runtime::function::Proto;
12use crate::vm::isa::{Inst, Op};
13
14fn kname(proto: &Proto, c: u32) -> Option<String> {
16 match proto.consts.get(c as usize) {
17 Some(Value::Str(s)) => Some(String::from_utf8_lossy(s.as_bytes()).into_owned()),
18 _ => None,
19 }
20}
21
22fn upvalname(proto: &Proto, u: u32) -> Option<String> {
23 proto.upvals.get(u as usize).map(|d| d.name.to_string())
24}
25
26fn writes_reg(i: Inst, reg: u32) -> bool {
28 let a = i.a();
29 match i.op() {
30 Op::LoadNil => a <= reg && reg <= a + i.b(),
31 Op::SelfOp => reg == a || reg == a + 1,
32 Op::Call | Op::TailCall | Op::Vararg => reg >= a,
34 Op::TForCall => reg >= a + 4,
35 Op::Jmp
37 | Op::SetUpval
38 | Op::SetTabUp
39 | Op::SetTable
40 | Op::SetI
41 | Op::SetField
42 | Op::Close
43 | Op::Tbc
44 | Op::Eq
45 | Op::Lt
46 | Op::Le
47 | Op::EqK
48 | Op::Test
49 | Op::Return
50 | Op::Return0
51 | Op::Return1
52 | Op::SetList
53 | Op::ExtraArg
54 | Op::TForPrep => false,
55 _ => reg == a,
56 }
57}
58
59fn find_setreg(proto: &Proto, lastpc: usize, reg: u32) -> Option<usize> {
63 let mut setreg: Option<usize> = None;
64 let mut jmptarget: usize = 0;
65 let code = &proto.code;
66 for pc in 0..lastpc {
67 let i = code[pc];
68 if i.op() == Op::Jmp {
69 let dest = (pc as i64 + 1 + i.sj() as i64) as usize;
70 if dest <= lastpc && dest > jmptarget {
71 jmptarget = dest;
72 }
73 continue;
74 }
75 if writes_reg(i, reg) {
76 setreg = if pc < jmptarget { None } else { Some(pc) };
77 }
78 }
79 setreg
80}
81
82fn basicgetobjname(proto: &Proto, lastpc: usize, reg: u32) -> Option<(&'static str, String)> {
87 if let Some(name) = getlocalname(proto, reg, lastpc) {
88 return Some(("local", name.to_string()));
89 }
90 let setpc = find_setreg(proto, lastpc, reg)?;
91 let i = proto.code[setpc];
92 match i.op() {
93 Op::Move => {
94 let b = i.b();
95 if b < i.a() {
96 basicgetobjname(proto, setpc, b)
97 } else {
98 None
99 }
100 }
101 Op::GetUpval => upvalname(proto, i.b()).map(|n| ("upvalue", n)),
102 Op::LoadK => kname(proto, i.bx()).map(|n| ("constant", n)),
103 Op::LoadKx => {
108 let next = proto.code.get(setpc + 1)?;
109 if next.op() != Op::ExtraArg {
110 return None;
111 }
112 kname(proto, next.ax()).map(|n| ("constant", n))
113 }
114 _ => None,
115 }
116}
117
118fn rname(proto: &Proto, pc: usize, c: u32) -> String {
122 match basicgetobjname(proto, pc, c) {
123 Some(("constant", n)) => n,
124 _ => "?".to_string(),
125 }
126}
127
128fn gxf(proto: &Proto, pc: usize, i: Inst, isup: bool) -> &'static str {
133 let t = i.b();
134 let tname = if isup {
135 upvalname(proto, t)
136 } else {
137 match basicgetobjname(proto, pc, t) {
138 Some((kind, n)) if kind == "local" || kind == "upvalue" => Some(n),
139 _ => None,
140 }
141 };
142 if tname.as_deref() == Some("_ENV") {
143 "global"
144 } else {
145 "field"
146 }
147}
148
149pub fn getlocalname(proto: &Proto, reg: u32, pc: usize) -> Option<&str> {
152 let pc = pc as u32;
153 proto
154 .locvars
155 .iter()
156 .filter(|lv| lv.reg == reg && lv.start_pc <= pc && pc < lv.end_pc)
157 .max_by_key(|lv| lv.start_pc)
158 .map(|lv| &*lv.name)
159}
160
161pub fn getobjname(proto: &Proto, lastpc: usize, reg: u32) -> Option<(&'static str, String)> {
164 if let Some(name) = getlocalname(proto, reg, lastpc) {
166 return Some(("local", name.to_string()));
167 }
168 let setpc = find_setreg(proto, lastpc, reg)?;
169 let i = proto.code[setpc];
170 match i.op() {
171 Op::Move => {
172 let b = i.b();
173 if b < i.a() {
175 getobjname(proto, setpc, b)
176 } else {
177 None
178 }
179 }
180 Op::GetUpval => upvalname(proto, i.b()).map(|n| ("upvalue", n)),
181 Op::GetTabUp => kname(proto, i.c()).map(|n| (gxf(proto, setpc, i, true), n)),
182 Op::GetField => kname(proto, i.c()).map(|n| (gxf(proto, setpc, i, false), n)),
183 Op::GetTable => Some((gxf(proto, setpc, i, false), rname(proto, setpc, i.c()))),
186 Op::GetI => Some(("field", "integer index".to_string())),
187 Op::SelfOp => {
188 let name = if i.k() {
193 kname(proto, i.c())
194 } else {
195 Some(rname(proto, setpc, i.c()))
196 };
197 name.map(|n| ("method", n))
198 }
199 _ => None,
200 }
201}