use crate::attributes::Attribute;
use crate::byte_reader::ByteReader;
use crate::constant_pool::ConstantPool;
use crate::display::indent_lines;
use crate::error::Result;
use crate::method_access_flags::MethodAccessFlags;
use byteorder::{BigEndian, WriteBytesExt};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Method {
pub access_flags: MethodAccessFlags,
pub name_index: u16,
pub descriptor_index: u16,
pub attributes: Vec<Attribute>,
}
impl Method {
pub fn from_bytes(
constant_pool: &ConstantPool<'_>,
bytes: &mut ByteReader<'_>,
) -> Result<Method> {
let access_flags = MethodAccessFlags::from_bits_truncate(bytes.read_u16()?);
let name_index = bytes.read_u16()?;
let descriptor_index = bytes.read_u16()?;
let attribute_count = bytes.read_u16()? as usize;
let mut attributes = Vec::with_capacity(attribute_count);
for _ in 0..attribute_count {
let attribute = Attribute::from_bytes(constant_pool, bytes)?;
attributes.push(attribute);
}
Ok(Method {
access_flags,
name_index,
descriptor_index,
attributes,
})
}
pub fn to_bytes(&self, bytes: &mut Vec<u8>) -> Result<()> {
self.access_flags.to_bytes(bytes)?;
bytes.write_u16::<BigEndian>(self.name_index)?;
bytes.write_u16::<BigEndian>(self.descriptor_index)?;
let attributes_length = u16::try_from(self.attributes.len())?;
bytes.write_u16::<BigEndian>(attributes_length)?;
for attribute in &self.attributes {
attribute.to_bytes(bytes)?;
}
Ok(())
}
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "flags: {}", self.access_flags)?;
writeln!(f, "name_index: #{}", self.name_index)?;
writeln!(f, "descriptor_index: #{}", self.descriptor_index)?;
writeln!(f, "attributes:")?;
for attribute in &self.attributes {
writeln!(f, "{}", indent_lines(&attribute.to_string(), " "))?;
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::attributes::Attribute;
use indoc::indoc;
#[test]
fn test_to_string() {
let attribute1 = Attribute::ConstantValue {
name_index: 1,
constant_value_index: 2,
};
let attribute2 = Attribute::ConstantValue {
name_index: 3,
constant_value_index: 4,
};
let method = Method {
access_flags: MethodAccessFlags::PUBLIC,
name_index: 1,
descriptor_index: 2,
attributes: vec![attribute1, attribute2],
};
let expected = indoc! {"
flags: (0x0001) ACC_PUBLIC
name_index: #1
descriptor_index: #2
attributes:
ConstantValue { name_index: 1, constant_value_index: 2 }
ConstantValue { name_index: 3, constant_value_index: 4 }
"};
assert_eq!(expected, method.to_string());
}
#[test]
fn test_serialization() -> Result<()> {
let mut constant_pool = ConstantPool::default();
constant_pool.add_utf8("ConstantValue")?;
let attribute_data = [0, 1, 0, 0, 0, 2, 4, 2].to_vec();
let mut attribute_bytes = ByteReader::new(&attribute_data);
let attribute = Attribute::from_bytes(&constant_pool, &mut attribute_bytes)?;
let method = Method {
access_flags: MethodAccessFlags::PUBLIC,
name_index: 1,
descriptor_index: 2,
attributes: vec![attribute],
};
let mut bytes = Vec::new();
method.to_bytes(&mut bytes)?;
let mut bytes = ByteReader::new(&bytes);
let result = Method::from_bytes(&constant_pool, &mut bytes)?;
assert_eq!(result, method);
Ok(())
}
}