use crate::class_file::{AttributeInfo, ClassFile, FieldInfo, MethodInfo};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Annotation {
pub type_name: String,
pub elements: HashMap<String, ElementValue>,
}
#[derive(Debug, Clone)]
pub enum ElementValue {
ConstInt(i32),
ConstLong(i64),
ConstFloat(f32),
ConstDouble(f64),
ConstString(String),
Enum(String, String),
Class(String),
Annotation(Annotation),
Array(Vec<ElementValue>),
}
fn find_attribute<'a>(
class: &'a ClassFile,
attrs: &'a [AttributeInfo],
name: &str,
) -> Option<&'a AttributeInfo> {
attrs
.iter()
.find(|a| class.get_string(a.attribute_name_index).as_deref() == Some(name))
}
pub fn parse_annotations(class: &ClassFile, attr: &AttributeInfo) -> Vec<Annotation> {
let data = &attr.info;
if data.len() < 2 {
return vec![];
}
let num = ((data[0] as usize) << 8) | (data[1] as usize);
let mut result = Vec::with_capacity(num);
let mut pos = 2usize;
for _ in 0..num {
if pos + 2 > data.len() {
break;
}
let type_index = ((data[pos] as u16) << 8) | (data[pos + 1] as u16);
pos += 2;
let type_name = class
.get_string(type_index)
.map(|s| s.replace('/', "."))
.unwrap_or_default();
if pos + 2 > data.len() {
break;
}
let num_pairs = ((data[pos] as usize) << 8) | (data[pos + 1] as usize);
pos += 2;
let mut elements = HashMap::new();
for _ in 0..num_pairs {
if pos + 4 > data.len() {
break;
}
let name_index = ((data[pos] as u16) << 8) | (data[pos + 1] as u16);
pos += 2;
let name = class.get_string(name_index).unwrap_or_default();
let ev = parse_element_value(class, data, &mut pos);
elements.insert(name, ev);
}
result.push(Annotation {
type_name,
elements,
});
}
result
}
fn parse_element_value(class: &ClassFile, data: &[u8], pos: &mut usize) -> ElementValue {
if *pos >= data.len() {
return ElementValue::ConstInt(0);
}
let tag = data[*pos];
*pos += 1;
let read_u16 = |p: &mut usize| -> u16 {
if *p + 2 <= data.len() {
let v = ((data[*p] as u16) << 8) | (data[*p + 1] as u16);
*p += 2;
v
} else {
0
}
};
match tag {
b'B' | b'C' | b'I' | b'S' | b'Z' => {
let _idx = read_u16(pos);
ElementValue::ConstInt(0)
}
b's' => {
let idx = read_u16(pos);
ElementValue::ConstString(class.get_string(idx).unwrap_or_default())
}
b'[' => {
let num = read_u16(pos) as usize;
let mut arr = Vec::with_capacity(num);
for _ in 0..num {
arr.push(parse_element_value(class, data, pos));
}
ElementValue::Array(arr)
}
_ => {
let _ = read_u16(pos);
ElementValue::ConstInt(0)
}
}
}
pub fn get_annotations(class: &ClassFile, attrs: &[AttributeInfo]) -> Vec<Annotation> {
let attr = find_attribute(class, attrs, "RuntimeVisibleAnnotations")
.or_else(|| find_attribute(class, attrs, "RuntimeInvisibleAnnotations"));
attr.map(|a| parse_annotations(class, a))
.unwrap_or_default()
}
pub fn get_signature(class: &ClassFile, attrs: &[AttributeInfo]) -> Option<String> {
let attr = find_attribute(class, attrs, "Signature")?;
if attr.info.len() >= 2 {
let idx = ((attr.info[0] as u16) << 8) | (attr.info[1] as u16);
class.get_string(idx)
} else {
None
}
}
impl ClassFile {
pub fn get_class_annotations(&self) -> Vec<Annotation> {
get_annotations(self, &self.attributes)
}
pub fn get_class_signature(&self) -> Option<String> {
get_signature(self, &self.attributes)
}
}
impl MethodInfo {
pub fn get_annotations(&self, class: &ClassFile) -> Vec<Annotation> {
get_annotations(class, &self.attributes)
}
pub fn get_signature_attr(&self, class: &ClassFile) -> Option<String> {
get_signature(class, &self.attributes)
}
}
impl FieldInfo {
pub fn get_annotations(&self, class: &ClassFile) -> Vec<Annotation> {
get_annotations(class, &self.attributes)
}
}