1use std::collections::HashSet;
12
13use crate::ir::expr::TypeRef;
14use crate::types::{GenericType, JavaType};
15
16#[derive(Debug, Clone)]
17pub struct VarInfo {
18 pub id: u32,
19 pub slot: u16,
20 pub name: String,
21 pub ty: TypeRef,
22 pub is_param: bool,
23 pub range_start: u16,
25 pub range_end: u16,
26 pub synthetic_name: bool,
28}
29
30#[derive(Debug, Clone, Default)]
31pub struct VarTable {
32 pub vars: Vec<VarInfo>,
33 pub by_slot: Vec<Vec<(u16, u16, u32)>>,
35 pub has_lvt: bool,
36 pub stack_vars: Vec<u32>,
38 pub wide_stack_vars: HashSet<u32>,
42 pub handler_starts: Vec<u16>,
51}
52
53impl VarTable {
54 pub fn add_stack_var(&mut self, slot: u16, name: String, ty: TypeRef) -> u32 {
55 let id = self.add(slot, name, ty, false, 0, u16::MAX, true);
56 self.stack_vars.push(id);
57 id
58 }
59
60 pub fn add_split(&mut self, slot: u16, name: String, ty: TypeRef) -> u32 {
65 let id = self.vars.len() as u32;
66 self.vars.push(VarInfo {
67 id,
68 slot,
69 name,
70 ty,
71 is_param: false,
72 range_start: 0,
73 range_end: u16::MAX,
74 synthetic_name: true,
75 });
76 id
77 }
78
79 pub fn add_catch_var(&mut self, slot: u16, name: String, ty: TypeRef) -> u32 {
80 self.add(slot, name, ty, false, 0, u16::MAX, true)
81 }
82
83 #[allow(clippy::too_many_arguments)] fn add(
85 &mut self,
86 slot: u16,
87 name: String,
88 ty: TypeRef,
89 is_param: bool,
90 rs: u16,
91 re: u16,
92 synth: bool,
93 ) -> u32 {
94 let id = self.vars.len() as u32;
95 self.vars.push(VarInfo {
96 id,
97 slot,
98 name,
99 ty,
100 is_param,
101 range_start: rs,
102 range_end: re,
103 synthetic_name: synth,
104 });
105 while self.by_slot.len() <= slot as usize {
106 self.by_slot.push(Vec::new());
107 }
108 self.by_slot[slot as usize].push((rs, re, id));
109 id
110 }
111
112 pub fn at(&self, slot: u16, pc: u16) -> Option<u32> {
115 let segs = self.by_slot.get(slot as usize)?;
116 for (rs, re, id) in segs {
117 if pc >= *rs && pc < *re {
118 return Some(*id);
119 }
120 }
121 for (rs, _, id) in segs {
130 if *rs > pc
131 && *rs - pc <= 8
132 && !self
133 .handler_starts
134 .iter()
135 .any(|h| *rs == *h || *rs == h + 1 || *rs == h + 2)
136 {
137 return Some(*id);
138 }
139 }
140 let mut best: Option<u32> = None;
141 for (rs, _, id) in segs {
142 if *rs <= pc {
143 best = Some(*id);
144 }
145 }
146 best.or_else(|| segs.first().map(|(_, _, id)| *id))
147 }
148
149 pub fn sole_on_slot(&self, slot: u16) -> Option<u32> {
151 let segs = self.by_slot.get(slot as usize)?;
152 if segs.len() == 1 {
153 Some(segs[0].2)
154 } else {
155 None
156 }
157 }
158
159 pub fn vars_on_slot(&self, slot: u16) -> &[(u16, u16, u32)] {
160 self.by_slot
161 .get(slot as usize)
162 .map(|v| v.as_slice())
163 .unwrap_or(&[])
164 }
165
166 pub fn var(&self, id: u32) -> &VarInfo {
167 self.vars.get(id as usize).unwrap_or_else(|| dummy_var(id))
168 }
169
170 pub fn var_mut(&mut self, id: u32) -> &mut VarInfo {
171 &mut self.vars[id as usize]
172 }
173}
174
175fn dummy_var(id: u32) -> &'static VarInfo {
183 use std::collections::HashMap;
184 use std::sync::{Mutex, OnceLock};
185 static DUMMIES: OnceLock<Mutex<HashMap<u32, &'static VarInfo>>> = OnceLock::new();
186 let map = DUMMIES.get_or_init(|| Mutex::new(HashMap::new()));
187 let mut g = map.lock().unwrap_or_else(|e| e.into_inner());
188 if let Some(&v) = g.get(&id) {
189 return v;
190 }
191 let v: &'static VarInfo = Box::leak(Box::new(VarInfo {
192 id,
193 slot: u16::MAX,
194 name: format!("var{}", id),
195 ty: TypeRef::J(crate::types::JavaType::Int),
196 is_param: false,
197 range_start: 0,
198 range_end: 0,
199 synthetic_name: true,
200 }));
201 g.insert(id, v);
202 v
203}
204
205pub fn sig_type_at(
206 lvtt: &[(u16, u16, String, u16)],
207 start: u16,
208 slot: u16,
209 base: &JavaType,
210) -> Option<TypeRef> {
211 lvtt.iter()
212 .find(|(s, _, _, sl)| *s == start && *sl == slot)
213 .and_then(|(_, _, sig, _)| crate::types::parse_field_signature(sig).map(TypeRef::G))
214 .filter(|tr| sig_matches_base(tr, base))
218}
219
220pub fn sig_matches_base(tr: &TypeRef, base: &JavaType) -> bool {
221 let g = match tr {
222 TypeRef::G(g) => g,
223 TypeRef::J(_) => return true,
224 };
225 match g {
226 crate::types::GenericType::TypeVar(_) => matches!(base, JavaType::Object(_)),
229 crate::types::GenericType::Class(cs) => {
230 matches!(base, JavaType::Object(n) if n.as_ref() == cs.internal_name())
231 }
232 crate::types::GenericType::Primitive(c) => base.primitive_char() == Some(*c),
233 crate::types::GenericType::Array(_) => matches!(base, JavaType::Array(_)),
234 crate::types::GenericType::Wildcard(_) => false,
235 }
236}
237
238pub fn unknown_ref_type() -> TypeRef {
240 TypeRef::G(GenericType::Class(crate::types::ClassSig {
241 package: "java/lang".into(),
242 parts: vec![crate::types::ClassSigPart {
243 name: "Object".into(),
244 args: vec![],
245 }],
246 }))
247}