use std::collections::HashSet;
use crate::ir::expr::TypeRef;
use crate::types::{GenericType, JavaType};
#[derive(Debug, Clone)]
pub struct VarInfo {
pub id: u32,
pub slot: u16,
pub name: String,
pub ty: TypeRef,
pub is_param: bool,
pub range_start: u16,
pub range_end: u16,
pub synthetic_name: bool,
}
#[derive(Debug, Clone, Default)]
pub struct VarTable {
pub vars: Vec<VarInfo>,
pub by_slot: Vec<Vec<(u16, u16, u32)>>,
pub has_lvt: bool,
pub stack_vars: Vec<u32>,
pub wide_stack_vars: HashSet<u32>,
pub force_concrete_vars: HashSet<u32>,
pub handler_starts: Vec<u16>,
}
impl VarTable {
pub fn add_stack_var(&mut self, slot: u16, name: String, ty: TypeRef) -> u32 {
let id = self.add(slot, name, ty, false, 0, u16::MAX, true);
self.stack_vars.push(id);
id
}
pub fn add_split(&mut self, slot: u16, name: String, ty: TypeRef) -> u32 {
let id = self.vars.len() as u32;
self.vars.push(VarInfo {
id,
slot,
name,
ty,
is_param: false,
range_start: 0,
range_end: u16::MAX,
synthetic_name: true,
});
id
}
pub fn add_catch_var(&mut self, slot: u16, name: String, ty: TypeRef) -> u32 {
self.add(slot, name, ty, false, 0, u16::MAX, true)
}
#[allow(clippy::too_many_arguments)] fn add(
&mut self,
slot: u16,
name: String,
ty: TypeRef,
is_param: bool,
rs: u16,
re: u16,
synth: bool,
) -> u32 {
let id = self.vars.len() as u32;
self.vars.push(VarInfo {
id,
slot,
name,
ty,
is_param,
range_start: rs,
range_end: re,
synthetic_name: synth,
});
while self.by_slot.len() <= slot as usize {
self.by_slot.push(Vec::new());
}
self.by_slot[slot as usize].push((rs, re, id));
id
}
pub fn at(&self, slot: u16, pc: u16) -> Option<u32> {
let segs = self.by_slot.get(slot as usize)?;
for (rs, re, id) in segs {
if pc >= *rs && pc < *re {
return Some(*id);
}
}
for (rs, _, id) in segs {
if *rs > pc
&& *rs - pc <= 8
&& !self
.handler_starts
.iter()
.any(|h| *rs == *h || *rs == h + 1 || *rs == h + 2)
{
return Some(*id);
}
}
let mut best: Option<u32> = None;
for (rs, _, id) in segs {
if *rs <= pc {
best = Some(*id);
}
}
best.or_else(|| segs.first().map(|(_, _, id)| *id))
}
pub fn sole_on_slot(&self, slot: u16) -> Option<u32> {
let segs = self.by_slot.get(slot as usize)?;
if segs.len() == 1 {
Some(segs[0].2)
} else {
None
}
}
pub fn vars_on_slot(&self, slot: u16) -> &[(u16, u16, u32)] {
self.by_slot
.get(slot as usize)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn var(&self, id: u32) -> &VarInfo {
self.vars.get(id as usize).unwrap_or_else(|| dummy_var(id))
}
pub fn var_mut(&mut self, id: u32) -> &mut VarInfo {
&mut self.vars[id as usize]
}
}
fn dummy_var(id: u32) -> &'static VarInfo {
use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
static DUMMIES: OnceLock<Mutex<HashMap<u32, &'static VarInfo>>> = OnceLock::new();
let map = DUMMIES.get_or_init(|| Mutex::new(HashMap::new()));
let mut g = map.lock().unwrap_or_else(|e| e.into_inner());
if let Some(&v) = g.get(&id) {
return v;
}
let v: &'static VarInfo = Box::leak(Box::new(VarInfo {
id,
slot: u16::MAX,
name: format!("var{}", id),
ty: TypeRef::J(crate::types::JavaType::Int),
is_param: false,
range_start: 0,
range_end: 0,
synthetic_name: true,
}));
g.insert(id, v);
v
}
pub fn sig_type_at(
lvtt: &[(u16, u16, String, u16)],
start: u16,
slot: u16,
base: &JavaType,
) -> Option<TypeRef> {
lvtt.iter()
.find(|(s, _, _, sl)| *s == start && *sl == slot)
.and_then(|(_, _, sig, _)| crate::types::parse_field_signature(sig).map(TypeRef::G))
.filter(|tr| sig_matches_base(tr, base))
}
pub fn sig_matches_base(tr: &TypeRef, base: &JavaType) -> bool {
let g = match tr {
TypeRef::G(g) => g,
TypeRef::J(_) => return true,
};
match g {
crate::types::GenericType::TypeVar(_) => matches!(base, JavaType::Object(_)),
crate::types::GenericType::Class(cs) => {
matches!(base, JavaType::Object(n) if n.as_ref() == cs.internal_name())
}
crate::types::GenericType::Primitive(c) => base.primitive_char() == Some(*c),
crate::types::GenericType::Array(_) => matches!(base, JavaType::Array(_)),
crate::types::GenericType::Wildcard(_) => false,
}
}
pub fn unknown_ref_type() -> TypeRef {
TypeRef::G(GenericType::Class(crate::types::ClassSig {
package: "java/lang".into(),
parts: vec![crate::types::ClassSigPart {
name: "Object".into(),
args: vec![],
}],
}))
}