use crate::constant_pool::{ConstantEntry, ConstantPool, NameAndType, Reference};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ConstantPoolBuilder {
entries: Vec<ConstantEntry>,
utf8_map: HashMap<String, u16>,
int_map: HashMap<i32, u16>,
class_map: HashMap<u16, u16>,
string_map: HashMap<u16, u16>,
nat_map: HashMap<(u16, u16), u16>,
methodref_map: HashMap<(u16, u16), u16>,
fieldref_map: HashMap<(u16, u16), u16>,
}
impl ConstantPoolBuilder {
pub fn new() -> Self {
Self {
entries: vec![ConstantEntry::Unused],
utf8_map: HashMap::new(),
int_map: HashMap::new(),
class_map: HashMap::new(),
string_map: HashMap::new(),
nat_map: HashMap::new(),
methodref_map: HashMap::new(),
fieldref_map: HashMap::new(),
}
}
fn add_entry(&mut self, entry: ConstantEntry) -> u16 {
let idx = self.entries.len() as u16;
let is_wide = matches!(entry, ConstantEntry::Long(_) | ConstantEntry::Double(_));
self.entries.push(entry);
if is_wide {
self.entries.push(ConstantEntry::Unused);
}
idx
}
fn reserve_entry(&mut self) -> u16 {
let idx = self.entries.len() as u16;
self.entries.push(ConstantEntry::Unused);
idx
}
fn fill_entry(&mut self, idx: u16, entry: ConstantEntry) {
debug_assert!(
matches!(self.entries[idx as usize], ConstantEntry::Unused),
"bug in cp builder"
);
self.entries[idx as usize] = entry;
}
pub fn add_utf8(&mut self, s: &str) -> u16 {
if let Some(&idx) = self.utf8_map.get(s) {
return idx;
}
let idx = self.add_entry(ConstantEntry::Utf8(s.to_string()));
self.utf8_map.insert(s.to_string(), idx);
idx
}
pub fn add_class(&mut self, name: &str) -> u16 {
if let Some(&utf8_idx) = self.utf8_map.get(name)
&& let Some(&idx) = self.class_map.get(&utf8_idx)
{
return idx;
}
let class_idx = self.reserve_entry();
let utf8_idx = self.add_utf8(name);
self.fill_entry(class_idx, ConstantEntry::Class(utf8_idx));
self.class_map.insert(utf8_idx, class_idx);
class_idx
}
pub fn add_string(&mut self, s: &str) -> u16 {
if let Some(&utf8_idx) = self.utf8_map.get(s)
&& let Some(&idx) = self.string_map.get(&utf8_idx)
{
return idx;
}
let string_idx = self.reserve_entry();
let utf8_idx = self.add_utf8(s);
self.fill_entry(string_idx, ConstantEntry::String(utf8_idx));
self.string_map.insert(utf8_idx, string_idx);
string_idx
}
pub fn add_integer(&mut self, value: i32) -> u16 {
if let Some(&idx) = self.int_map.get(&value) {
return idx;
}
let idx = self.add_entry(ConstantEntry::Integer(value));
self.int_map.insert(value, idx);
idx
}
pub fn add_methodref(&mut self, class: &str, name: &str, descriptor: &str) -> u16 {
if let Some(idx) = self.find_ref(&self.methodref_map, class, name, descriptor) {
return idx;
}
let (ref_idx, class_idx, nat_idx) = self.add_ref_entries(class, name, descriptor);
self.fill_entry(
ref_idx,
ConstantEntry::MethodRef(Reference {
class_index: class_idx,
name_and_type_index: nat_idx,
}),
);
self.methodref_map.insert((class_idx, nat_idx), ref_idx);
ref_idx
}
pub fn add_fieldref(&mut self, class: &str, name: &str, descriptor: &str) -> u16 {
if let Some(idx) = self.find_ref(&self.fieldref_map, class, name, descriptor) {
return idx;
}
let (ref_idx, class_idx, nat_idx) = self.add_ref_entries(class, name, descriptor);
self.fill_entry(
ref_idx,
ConstantEntry::FieldRef(Reference {
class_index: class_idx,
name_and_type_index: nat_idx,
}),
);
self.fieldref_map.insert((class_idx, nat_idx), ref_idx);
ref_idx
}
fn add_ref_entries(&mut self, class: &str, name: &str, descriptor: &str) -> (u16, u16, u16) {
let ref_idx = self.reserve_entry();
let class_idx = self.find_or_reserve_class(class);
let nat_idx = self.find_or_reserve_nat(name, descriptor);
let class_name_idx = self.add_utf8(class);
let name_idx = self.add_utf8(name);
let desc_idx = self.add_utf8(descriptor);
self.fill_class_if_reserved(class_idx, class_name_idx);
self.fill_nat_if_reserved(nat_idx, name_idx, desc_idx);
(ref_idx, class_idx, nat_idx)
}
fn find_or_reserve_class(&mut self, name: &str) -> u16 {
if let Some(&name_idx) = self.utf8_map.get(name)
&& let Some(&idx) = self.class_map.get(&name_idx)
{
return idx;
}
self.reserve_entry()
}
fn find_or_reserve_nat(&mut self, name: &str, descriptor: &str) -> u16 {
if let Some(&name_idx) = self.utf8_map.get(name)
&& let Some(&desc_idx) = self.utf8_map.get(descriptor)
&& let Some(&idx) = self.nat_map.get(&(name_idx, desc_idx))
{
return idx;
}
self.reserve_entry()
}
fn fill_class_if_reserved(&mut self, class_idx: u16, name_idx: u16) {
if matches!(self.entries[class_idx as usize], ConstantEntry::Unused) {
self.entries[class_idx as usize] = ConstantEntry::Class(name_idx);
self.class_map.insert(name_idx, class_idx);
}
}
fn fill_nat_if_reserved(&mut self, nat_idx: u16, name_idx: u16, desc_idx: u16) {
if matches!(self.entries[nat_idx as usize], ConstantEntry::Unused) {
self.entries[nat_idx as usize] = ConstantEntry::NameAndType(NameAndType {
name_index: name_idx,
descriptor_index: desc_idx,
});
self.nat_map.insert((name_idx, desc_idx), nat_idx);
}
}
fn find_ref(
&self,
ref_map: &HashMap<(u16, u16), u16>,
class: &str,
name: &str,
descriptor: &str,
) -> Option<u16> {
let name_idx = *self.utf8_map.get(class)?;
let class_idx = *self.class_map.get(&name_idx)?;
let method_name_idx = *self.utf8_map.get(name)?;
let desc_idx = *self.utf8_map.get(descriptor)?;
let nat_idx = *self.nat_map.get(&(method_name_idx, desc_idx))?;
ref_map.get(&(class_idx, nat_idx)).copied()
}
pub fn build(self) -> ConstantPool {
ConstantPool {
inner: self.entries,
}
}
pub fn len(&self) -> u16 {
self.entries.len() as u16
}
pub fn is_empty(&self) -> bool {
self.entries.len() <= 1
}
}
impl Default for ConstantPoolBuilder {
fn default() -> Self {
Self::new()
}
}