use crate::Error::{InvalidConstantPoolIndex, InvalidConstantPoolIndexType};
use crate::ReferenceKind;
use crate::byte_reader::ByteReader;
use crate::constant::Constant;
use crate::error::Result;
use crate::java_string::{JavaStr, JavaString};
use byteorder::{BigEndian, WriteBytesExt};
use std::borrow::Cow;
use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ConstantPool<'a> {
constants: Vec<ConstantEntry<'a>>,
}
impl<'a> ConstantPool<'a> {
#[must_use]
pub fn new() -> Self {
Self {
constants: vec![ConstantEntry::Placeholder],
}
}
#[must_use]
pub fn into_owned(self) -> ConstantPool<'static> {
ConstantPool {
constants: self
.constants
.into_iter()
.map(|entry| match entry {
ConstantEntry::Constant(c) => ConstantEntry::Constant(c.into_owned()),
ConstantEntry::Placeholder => ConstantEntry::Placeholder,
})
.collect(),
}
}
pub fn push(&mut self, constant: Constant<'a>) {
let add_placeholder = matches!(constant, Constant::Long(_) | Constant::Double(_));
self.constants.push(ConstantEntry::Constant(constant));
if add_placeholder {
self.constants.push(ConstantEntry::Placeholder);
}
}
pub fn add(&mut self, constant: Constant<'a>) -> Result<u16> {
let index = u16::try_from(self.constants.len())?;
self.push(constant);
Ok(index)
}
#[must_use]
pub fn get(&self, index: u16) -> Option<&Constant<'a>> {
self.try_get(index).ok()
}
pub fn try_get(&self, index: u16) -> Result<&Constant<'a>> {
let constant_entry = self.constants.get(index as usize);
match constant_entry {
Some(ConstantEntry::Constant(constant)) => Ok(constant),
_ => Err(InvalidConstantPoolIndex(index)),
}
}
pub fn set(&mut self, index: u16, constant: Constant<'a>) -> Result<()> {
let constant_entry = self.constants.get_mut(index as usize);
match constant_entry {
Some(entry @ ConstantEntry::Constant(_)) => {
*entry = ConstantEntry::Constant(constant);
Ok(())
}
_ => Err(InvalidConstantPoolIndex(index)),
}
}
#[must_use]
pub fn len(&self) -> usize {
self.constants.len() - 1
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn iter<'b>(&'b self) -> ConstantPoolIterator<'a, 'b> {
ConstantPoolIterator::new(self)
}
#[inline]
pub fn from_bytes<'b>(bytes: &mut ByteReader<'b>) -> Result<ConstantPool<'b>> {
let raw_count = bytes.read_u16()?;
if raw_count == 0 {
return Err(crate::Error::IoError(
"Invalid constant pool count".to_string(),
));
}
let target_len = raw_count as usize;
let mut constants = Vec::with_capacity(target_len);
constants.push(ConstantEntry::Placeholder);
while constants.len() < target_len {
let constant = Constant::from_bytes(bytes)?;
let add_placeholder = matches!(constant, Constant::Long(_) | Constant::Double(_));
constants.push(ConstantEntry::Constant(constant));
if add_placeholder {
constants.push(ConstantEntry::Placeholder);
}
}
Ok(ConstantPool { constants })
}
fn with_capacity(capacity: usize) -> Self {
let mut constants = Vec::with_capacity(capacity + 1);
constants.push(ConstantEntry::Placeholder);
Self { constants }
}
#[inline]
pub(crate) fn get_unchecked(&self, index: u16) -> Option<&Constant<'a>> {
let idx = index as usize;
if idx < self.constants.len() {
#[expect(unsafe_code)]
match unsafe { self.constants.get_unchecked(idx) } {
ConstantEntry::Constant(c) => Some(c),
ConstantEntry::Placeholder => None,
}
} else {
None
}
}
pub fn to_bytes(&self, bytes: &mut Vec<u8>) -> Result<()> {
let constant_pool_count = u16::try_from(self.len())? + 1;
bytes.write_u16::<BigEndian>(constant_pool_count)?;
for constant_entry in &self.constants {
if let ConstantEntry::Constant(constant) = constant_entry {
constant.to_bytes(bytes)?;
}
}
Ok(())
}
pub fn add_utf8<S: AsRef<str>>(&mut self, value: S) -> Result<u16> {
let java_string = JavaString::from(value.as_ref());
self.add(Constant::Utf8(Cow::Owned(java_string)))
}
pub fn try_get_utf8(&self, index: u16) -> Result<&JavaStr> {
match self.try_get(index)? {
Constant::Utf8(value) => Ok(value.as_ref()),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_integer(&mut self, value: i32) -> Result<u16> {
self.add(Constant::Integer(value))
}
pub fn try_get_integer(&self, index: u16) -> Result<&i32> {
match self.try_get(index)? {
Constant::Integer(value) => Ok(value),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_float(&mut self, value: f32) -> Result<u16> {
self.add(Constant::Float(value))
}
pub fn try_get_float(&self, index: u16) -> Result<&f32> {
match self.try_get(index)? {
Constant::Float(value) => Ok(value),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_long(&mut self, value: i64) -> Result<u16> {
self.add(Constant::Long(value))
}
pub fn try_get_long(&self, index: u16) -> Result<&i64> {
match self.try_get(index)? {
Constant::Long(value) => Ok(value),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_double(&mut self, value: f64) -> Result<u16> {
self.add(Constant::Double(value))
}
pub fn try_get_double(&self, index: u16) -> Result<&f64> {
match self.try_get(index)? {
Constant::Double(value) => Ok(value),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_class<S: AsRef<str>>(&mut self, name: S) -> Result<u16> {
let utf8_index = self.add_utf8(name)?;
self.add(Constant::Class(utf8_index))
}
pub fn try_get_class(&self, index: u16) -> Result<&JavaStr> {
match self.try_get(index)? {
Constant::Class(utf8_index) => self.try_get_utf8(*utf8_index),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_string<S: AsRef<str>>(&mut self, name: S) -> Result<u16> {
let utf8_index = self.add_utf8(name)?;
self.add(Constant::String(utf8_index))
}
pub fn try_get_string(&self, index: u16) -> Result<&JavaStr> {
match self.try_get(index)? {
Constant::String(value) => self.try_get_utf8(*value),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_field_ref<S: AsRef<str>>(
&mut self,
class_index: u16,
name: S,
descriptor: S,
) -> Result<u16> {
let name_and_type_index = self.add_name_and_type(name, descriptor)?;
self.add(Constant::FieldRef {
class_index,
name_and_type_index,
})
}
pub fn try_get_field_ref(&self, index: u16) -> Result<(&u16, &u16)> {
match self.try_get(index)? {
Constant::FieldRef {
class_index,
name_and_type_index,
} => Ok((class_index, name_and_type_index)),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_method_ref<S: AsRef<str>>(
&mut self,
class_index: u16,
name: S,
descriptor: S,
) -> Result<u16> {
let name_and_type_index = self.add_name_and_type(name, descriptor)?;
self.add(Constant::MethodRef {
class_index,
name_and_type_index,
})
}
pub fn try_get_method_ref(&self, index: u16) -> Result<(&u16, &u16)> {
match self.try_get(index)? {
Constant::MethodRef {
class_index,
name_and_type_index,
} => Ok((class_index, name_and_type_index)),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_interface_method_ref<S: AsRef<str>>(
&mut self,
class_index: u16,
name: S,
descriptor: S,
) -> Result<u16> {
let name_and_type_index = self.add_name_and_type(name, descriptor)?;
self.add(Constant::InterfaceMethodRef {
class_index,
name_and_type_index,
})
}
pub fn try_get_interface_method_ref(&self, index: u16) -> Result<(&u16, &u16)> {
match self.try_get(index)? {
Constant::InterfaceMethodRef {
class_index,
name_and_type_index,
} => Ok((class_index, name_and_type_index)),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_name_and_type<S: AsRef<str>>(&mut self, name: S, descriptor: S) -> Result<u16> {
let name_index = self.add_utf8(name)?;
let descriptor_index = self.add_utf8(descriptor)?;
self.add(Constant::NameAndType {
name_index,
descriptor_index,
})
}
pub fn try_get_name_and_type(&self, index: u16) -> Result<(&u16, &u16)> {
match self.try_get(index)? {
Constant::NameAndType {
name_index,
descriptor_index,
} => Ok((name_index, descriptor_index)),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_method_handle(
&mut self,
reference_kind: ReferenceKind,
reference_index: u16,
) -> Result<u16> {
self.add(Constant::MethodHandle {
reference_kind,
reference_index,
})
}
pub fn try_get_method_handle(&self, index: u16) -> Result<(&ReferenceKind, &u16)> {
match self.try_get(index)? {
Constant::MethodHandle {
reference_kind,
reference_index,
} => Ok((reference_kind, reference_index)),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_method_type<S: AsRef<str>>(&mut self, name: S) -> Result<u16> {
let utf8_index = self.add_utf8(name)?;
self.add(Constant::MethodType(utf8_index))
}
pub fn try_get_method_type(&self, index: u16) -> Result<&u16> {
match self.try_get(index)? {
Constant::MethodType(name_and_type_index) => Ok(name_and_type_index),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_dynamic<S: AsRef<str>>(
&mut self,
bootstrap_method_attr_index: u16,
name: S,
descriptor: S,
) -> Result<u16> {
let name_and_type_index = self.add_name_and_type(name, descriptor)?;
self.add(Constant::Dynamic {
bootstrap_method_attr_index,
name_and_type_index,
})
}
pub fn try_get_dynamic(&self, index: u16) -> Result<(&u16, &u16)> {
match self.try_get(index)? {
Constant::Dynamic {
bootstrap_method_attr_index,
name_and_type_index,
} => Ok((bootstrap_method_attr_index, name_and_type_index)),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_invoke_dynamic<S: AsRef<str>>(
&mut self,
bootstrap_method_attr_index: u16,
name: S,
descriptor: S,
) -> Result<u16> {
let name_and_type_index = self.add_name_and_type(name, descriptor)?;
self.add(Constant::InvokeDynamic {
bootstrap_method_attr_index,
name_and_type_index,
})
}
pub fn try_get_invoke_dynamic(&self, index: u16) -> Result<(&u16, &u16)> {
match self.try_get(index)? {
Constant::InvokeDynamic {
bootstrap_method_attr_index,
name_and_type_index,
} => Ok((bootstrap_method_attr_index, name_and_type_index)),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_module<S: AsRef<str>>(&mut self, name: S) -> Result<u16> {
let utf8_index = self.add_utf8(name)?;
self.add(Constant::Module(utf8_index))
}
pub fn try_get_module(&self, index: u16) -> Result<&JavaStr> {
match self.try_get(index)? {
Constant::Module(name_index) => self.try_get_utf8(*name_index),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn add_package<S: AsRef<str>>(&mut self, name: S) -> Result<u16> {
let utf8_index = self.add_utf8(name)?;
self.add(Constant::Package(utf8_index))
}
pub fn try_get_package(&self, index: u16) -> Result<&JavaStr> {
match self.try_get(index)? {
Constant::Package(name_index) => self.try_get_utf8(*name_index),
_ => Err(InvalidConstantPoolIndexType(index)),
}
}
pub fn try_get_formatted_string(&self, index: u16) -> Result<String> {
let value = match self.try_get(index)? {
Constant::Utf8(value) => value.to_string(),
Constant::Integer(integer) => format!("{integer}"),
Constant::Float(float) => format!("{float}"),
Constant::Long(long) => format!("{long}"),
Constant::Double(double) => format!("{double}"),
Constant::Class(utf8_index) => {
format!("Class {}", self.try_get_utf8(*utf8_index)?)
}
Constant::String(utf8_index) => {
format!("String {}", self.try_get_utf8(*utf8_index)?)
}
Constant::FieldRef {
class_index,
name_and_type_index,
} => {
let (name_index, _descriptor_index) =
self.try_get_name_and_type(*name_and_type_index)?;
let class_name = self.try_get_class(*class_index)?;
let field_name = self.try_get_utf8(*name_index)?;
format!("Field {class_name}.{field_name}")
}
Constant::MethodRef {
class_index,
name_and_type_index,
} => {
let class_name = self.try_get_class(*class_index)?;
let (name_index, descriptor_index) =
self.try_get_name_and_type(*name_and_type_index)?;
let method_name = self.try_get_utf8(*name_index)?;
let method_descriptor = self.try_get_utf8(*descriptor_index)?;
format!("Method {class_name}.{method_name}{method_descriptor}")
}
Constant::InterfaceMethodRef {
class_index,
name_and_type_index,
} => {
let class_name = self.try_get_class(*class_index)?;
let (name_index, descriptor_index) =
self.try_get_name_and_type(*name_and_type_index)?;
let method_name = self.try_get_utf8(*name_index)?;
let method_descriptor = self.try_get_utf8(*descriptor_index)?;
format!("Interface method {class_name}.{method_name}{method_descriptor}")
}
Constant::NameAndType {
name_index,
descriptor_index,
} => {
let name = self.try_get_utf8(*name_index)?;
let descriptor = self.try_get_utf8(*descriptor_index)?;
format!("Name {name}, Descriptor {descriptor}")
}
Constant::MethodHandle {
reference_kind,
reference_index,
} => {
let reference = self.try_get_formatted_string(*reference_index)?;
format!("Method handle {reference_kind} {reference}")
}
Constant::MethodType(utf8_index) => {
let method_descriptor = self.try_get_utf8(*utf8_index)?;
format!("Method type {method_descriptor}")
}
Constant::Dynamic {
bootstrap_method_attr_index,
name_and_type_index,
} => {
let (name_index, descriptor_index) =
self.try_get_name_and_type(*name_and_type_index)?;
let name = self.try_get_utf8(*name_index)?;
let descriptor = self.try_get_utf8(*descriptor_index)?;
format!("Dynamic #{bootstrap_method_attr_index}:{name}:{descriptor}")
}
Constant::InvokeDynamic {
bootstrap_method_attr_index,
name_and_type_index,
} => {
let (name_index, descriptor_index) =
self.try_get_name_and_type(*name_and_type_index)?;
let name = self.try_get_utf8(*name_index)?;
let descriptor = self.try_get_utf8(*descriptor_index)?;
format!("InvokeDynamic #{bootstrap_method_attr_index}:{name}:{descriptor}")
}
Constant::Module(name_index) => {
format!("Module {}", self.try_get_utf8(*name_index)?)
}
Constant::Package(name_index) => {
format!("Package {}", self.try_get_utf8(*name_index)?)
}
};
Ok(value)
}
}
impl Default for ConstantPool<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum ConstantEntry<'a> {
Constant(Constant<'a>),
Placeholder,
}
impl fmt::Display for ConstantEntry<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConstantEntry::Constant(constant) => write!(f, "{constant}"),
ConstantEntry::Placeholder => Ok(()),
}
}
}
#[derive(Debug)]
pub struct ConstantPoolIterator<'a, 'b> {
constant_pool: &'b ConstantPool<'a>,
index: usize,
}
impl<'a, 'b> ConstantPoolIterator<'a, 'b> {
pub fn new(constant_pool: &'b ConstantPool<'a>) -> Self {
Self {
constant_pool,
index: 1,
}
}
}
impl<'a, 'b> Iterator for ConstantPoolIterator<'a, 'b> {
type Item = &'b Constant<'a>;
fn next(&mut self) -> Option<Self::Item> {
while self.index < self.constant_pool.constants.len() {
match &self.constant_pool.constants[self.index] {
ConstantEntry::Constant(constant) => {
self.index += 1;
return Some(constant);
}
ConstantEntry::Placeholder => {
self.index += 1;
}
}
}
None
}
}
impl<'a, 'b> IntoIterator for &'b ConstantPool<'a> {
type Item = &'b Constant<'a>;
type IntoIter = ConstantPoolIterator<'a, 'b>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl fmt::Display for ConstantPool<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (index, constant_entry) in self.constants.iter().skip(1).enumerate() {
match constant_entry {
ConstantEntry::Constant(constant) => {
let index = index + 1;
let value = constant.to_string();
let (name, value) = value.split_once(' ').unwrap_or_default();
writeln!(f, "{:>5} = {name:<18} {value}", format!("#{index}"))?;
}
ConstantEntry::Placeholder => {}
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::Error::IoError;
use crate::constant::Constant;
use std::fmt::Debug;
#[test]
fn test_constant_pool_entry_to_string() {
assert_eq!(
"Integer 42",
ConstantEntry::Constant(Constant::Integer(42)).to_string()
);
assert_eq!("", ConstantEntry::Placeholder.to_string());
}
#[test]
fn test_get_zero_none() {
let constant_pool = ConstantPool::default();
assert!(constant_pool.get(0).is_none());
}
#[test]
fn test_get() {
let mut constant_pool = ConstantPool::default();
assert!(constant_pool.get(1).is_none());
constant_pool.push(Constant::utf8("foo"));
assert!(constant_pool.get(1).is_some());
}
#[test]
fn test_try_get_zero_error() {
let constant_pool = ConstantPool::default();
assert_eq!(Err(InvalidConstantPoolIndex(0)), constant_pool.try_get(0));
}
#[test]
fn test_try_get() {
let mut constant_pool = ConstantPool::default();
assert!(constant_pool.try_get(1).is_err());
constant_pool.push(Constant::utf8("foo"));
assert!(constant_pool.try_get(1).is_ok());
}
#[test]
fn test_set() {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::utf8("foo"));
assert_eq!(Some(&Constant::utf8("foo")), constant_pool.get(1));
constant_pool
.set(1, Constant::utf8("baz"))
.expect("Failed to set constant");
assert_eq!(Some(&Constant::utf8("baz")), constant_pool.get(1));
}
#[test]
fn test_utf8() {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::utf8("foo"));
assert!(constant_pool.get(1).is_some());
assert_eq!(1, constant_pool.len());
}
#[test]
fn test_integer() {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::Integer(42));
assert!(constant_pool.get(1).is_some());
assert_eq!(1, constant_pool.len());
}
#[test]
fn test_long() {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::Long(1_234_567_890));
assert!(constant_pool.get(1).is_some());
assert!(constant_pool.get(2).is_none());
assert_eq!(2, constant_pool.len());
}
#[test]
fn test_len() {
let mut constant_pool = ConstantPool::default();
assert_eq!(0, constant_pool.len());
constant_pool.push(Constant::Integer(42));
assert_eq!(1, constant_pool.len());
}
#[test]
fn test_is_empty() {
let mut constant_pool = ConstantPool::default();
assert!(constant_pool.is_empty());
constant_pool.push(Constant::Integer(42));
assert!(!constant_pool.is_empty());
}
#[test]
fn test_iter() {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::utf8("foo"));
constant_pool.push(Constant::Integer(42));
constant_pool.push(Constant::Long(1_234_567_890));
let mut iter = constant_pool.iter();
assert_eq!(Some(&Constant::utf8("foo")), iter.next());
assert_eq!(Some(&Constant::Integer(42)), iter.next());
assert_eq!(Some(&Constant::Long(1_234_567_890)), iter.next());
assert_eq!(None, iter.next());
}
#[test]
fn test_into_iter() {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::utf8("foo"));
for constant in &constant_pool {
assert_eq!(Constant::utf8("foo"), *constant);
}
}
#[test]
fn test_double() {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::Double(std::f64::consts::PI));
assert!(constant_pool.get(1).is_some());
assert!(constant_pool.get(2).is_none());
assert_eq!(2, constant_pool.len());
}
#[test]
fn test_to_string() {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::utf8("foo"));
constant_pool.push(Constant::Integer(42));
constant_pool.push(Constant::Long(1_234_567_890));
let expected = " #1 = Utf8 foo\n #2 = Integer 42\n #3 = Long 1234567890\n";
assert_eq!(expected, constant_pool.to_string());
}
#[test]
fn test_serialization() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let integer_constant = Constant::Integer(42);
let long_constant = Constant::Long(1_234_567_890);
constant_pool.push(integer_constant.clone());
constant_pool.push(long_constant.clone());
let expected_bytes = [0, 4, 3, 0, 0, 0, 42, 5, 0, 0, 0, 0, 73, 150, 2, 210];
let mut bytes = Vec::new();
constant_pool.to_bytes(&mut bytes)?;
assert_eq!(expected_bytes, &bytes[..]);
let mut bytes = ByteReader::new(&expected_bytes);
assert_eq!(constant_pool, ConstantPool::from_bytes(&mut bytes)?);
Ok(())
}
fn test_try_get_constant<T>(
f: for<'a> fn(&'a ConstantPool<'static>, u16) -> Result<&'a T>,
constant: Constant<'static>,
) where
T: Debug + PartialEq + ?Sized,
{
let mut constant_pool = ConstantPool::default();
if matches!(constant, Constant::Utf8(_)) {
constant_pool.push(Constant::Integer(42));
} else {
constant_pool.push(Constant::utf8("foo"));
}
constant_pool.push(constant);
assert_eq!(Err(InvalidConstantPoolIndex(0)), f(&constant_pool, 0));
assert_eq!(Err(InvalidConstantPoolIndexType(1)), f(&constant_pool, 1));
assert!(f(&constant_pool, 2).is_ok());
}
fn test_try_get_constant_tuple<A, B>(
f: for<'a> fn(&'a ConstantPool<'static>, u16) -> Result<(&'a A, &'a B)>,
constant: Constant<'static>,
) where
A: Debug + PartialEq,
B: Debug + PartialEq,
{
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::utf8("foo"));
constant_pool.push(constant);
assert_eq!(Err(InvalidConstantPoolIndex(0)), f(&constant_pool, 0));
assert_eq!(Err(InvalidConstantPoolIndexType(1)), f(&constant_pool, 1));
assert!(f(&constant_pool, 2).is_ok());
}
#[test]
fn test_add_utf8() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_utf8("foo")?;
assert_eq!(1, index);
assert_eq!(Some(&Constant::utf8("foo")), constant_pool.get(index));
Ok(())
}
#[test]
fn test_try_get_utf8() {
test_try_get_constant(ConstantPool::try_get_utf8, Constant::utf8("foo"));
}
#[test]
fn test_try_get_formatted_string_utf8() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_utf8("foo")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("foo", value);
Ok(())
}
#[test]
fn test_add_integer() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_integer(42)?;
assert_eq!(1, index);
assert_eq!(Some(&Constant::Integer(42)), constant_pool.get(index));
Ok(())
}
#[test]
fn test_try_get_integer() {
test_try_get_constant(ConstantPool::try_get_integer, Constant::Integer(42));
}
#[test]
fn test_try_get_formatted_string_integer() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_integer(42)?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("42", value);
Ok(())
}
#[test]
fn test_add_float() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_float(std::f32::consts::PI)?;
assert_eq!(1, index);
assert_eq!(
Some(&Constant::Float(std::f32::consts::PI)),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_float() {
test_try_get_constant(
ConstantPool::try_get_float,
Constant::Float(std::f32::consts::PI),
);
}
#[test]
fn test_try_get_formatted_string_float() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_float(std::f32::consts::PI)?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("3.1415927", value);
Ok(())
}
#[test]
fn test_add_long() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_long(i64::MAX)?;
assert_eq!(1, index);
assert_eq!(Some(&Constant::Long(i64::MAX)), constant_pool.get(index));
Ok(())
}
#[test]
fn test_try_get_long() {
test_try_get_constant(ConstantPool::try_get_long, Constant::Long(i64::MAX));
}
#[test]
fn test_try_get_formatted_string_long() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_long(42)?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("42", value);
Ok(())
}
#[test]
fn test_add_double() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_double(std::f64::consts::PI)?;
assert_eq!(1, index);
assert_eq!(
Some(&Constant::Double(std::f64::consts::PI)),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_double() {
test_try_get_constant(
ConstantPool::try_get_double,
Constant::Double(std::f64::consts::PI),
);
}
#[test]
fn test_try_get_formatted_string_double() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_double(std::f64::consts::PI)?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("3.141592653589793", value);
Ok(())
}
#[test]
fn test_add_class() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_class("java/lang/Object")?;
assert_eq!(2, index);
assert_eq!(Some(&Constant::Class(1)), constant_pool.get(index));
Ok(())
}
#[test]
fn test_try_get_class() {
test_try_get_constant(ConstantPool::try_get_class, Constant::Class(1));
}
#[test]
fn test_try_get_formatted_string_class() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_class("Foo")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Class Foo", value);
Ok(())
}
#[test]
fn test_try_get_class_name() -> Result<()> {
let mut constant_pool = ConstantPool::default();
constant_pool.push(Constant::utf8("java/lang/Object"));
constant_pool.push(Constant::Class(1));
let class_name = constant_pool.try_get_class(2)?;
assert_eq!("java/lang/Object", class_name);
Ok(())
}
#[test]
fn test_add_string() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_string("foo")?;
assert_eq!(2, index);
assert_eq!(Some(&Constant::String(1)), constant_pool.get(index));
Ok(())
}
#[test]
fn test_try_get_string() {
test_try_get_constant(ConstantPool::try_get_string, Constant::String(1));
}
#[test]
fn test_try_get_formatted_string_string() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_string("foo")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("String foo", value);
Ok(())
}
#[test]
fn test_add_field_ref() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_field_ref(1, "out", "Ljava/io/PrintStream;")?;
assert_eq!(4, index);
assert_eq!(
Some(&Constant::FieldRef {
class_index: 1,
name_and_type_index: 3
}),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_field_ref() {
test_try_get_constant_tuple(
ConstantPool::try_get_field_ref,
Constant::FieldRef {
class_index: 1,
name_and_type_index: 3,
},
);
}
#[test]
fn test_try_get_formatted_string_field_ref() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let class_index = constant_pool.add_class("Foo")?;
let index = constant_pool.add_field_ref(class_index, "x", "I")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Field Foo.x", value);
Ok(())
}
#[test]
fn test_add_method_ref() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_method_ref(1, "println", "(Ljava/lang/String;)V")?;
assert_eq!(4, index);
assert_eq!(
Some(&Constant::MethodRef {
class_index: 1,
name_and_type_index: 3
}),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_method_ref() {
test_try_get_constant_tuple(
ConstantPool::try_get_method_ref,
Constant::MethodRef {
class_index: 1,
name_and_type_index: 3,
},
);
}
#[test]
fn test_try_get_formatted_string_method_ref() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let class_index = constant_pool.add_class("Foo")?;
let index = constant_pool.add_method_ref(class_index, "x", "()V")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Method Foo.x()V", value);
Ok(())
}
#[test]
fn test_add_interface_method_ref() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index =
constant_pool.add_interface_method_ref(1, "println", "(Ljava/lang/String;)V")?;
assert_eq!(4, index);
assert_eq!(
Some(&Constant::InterfaceMethodRef {
class_index: 1,
name_and_type_index: 3
}),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_interface_method_ref() {
test_try_get_constant_tuple(
ConstantPool::try_get_interface_method_ref,
Constant::InterfaceMethodRef {
class_index: 1,
name_and_type_index: 3,
},
);
}
#[test]
fn test_try_get_formatted_string_interface_method_ref() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let class_index = constant_pool.add_class("Foo")?;
let index = constant_pool.add_interface_method_ref(class_index, "x", "()V")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Interface method Foo.x()V", value);
Ok(())
}
#[test]
fn test_add_name_and_type() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_name_and_type("name", "type")?;
assert_eq!(3, index);
assert_eq!(
Some(&Constant::NameAndType {
name_index: 1,
descriptor_index: 2
}),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_name_and_type() {
test_try_get_constant_tuple(
ConstantPool::try_get_name_and_type,
Constant::NameAndType {
name_index: 1,
descriptor_index: 2,
},
);
}
#[test]
fn test_try_get_formatted_string_name_and_type() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_name_and_type("x", "I")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Name x, Descriptor I", value);
Ok(())
}
#[test]
fn test_add_method_handle() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_method_handle(ReferenceKind::GetField, 1)?;
assert_eq!(1, index);
assert_eq!(
Some(&Constant::MethodHandle {
reference_kind: ReferenceKind::GetField,
reference_index: 1
}),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_method_handle() {
test_try_get_constant_tuple(
ConstantPool::try_get_method_handle,
Constant::MethodHandle {
reference_kind: ReferenceKind::GetField,
reference_index: 1,
},
);
}
#[test]
fn test_try_get_formatted_string_method_handle() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let class_index = constant_pool.add_class("Foo")?;
let field_index = constant_pool.add_field_ref(class_index, "x", "I")?;
let index = constant_pool.add_method_handle(ReferenceKind::GetField, field_index)?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Method handle GetField Field Foo.x", value);
Ok(())
}
#[test]
fn test_add_method_type() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_method_type("()V")?;
assert_eq!(2, index);
assert_eq!(Some(&Constant::MethodType(1)), constant_pool.get(index));
Ok(())
}
#[test]
fn test_try_get_method_type() {
test_try_get_constant(ConstantPool::try_get_method_type, Constant::MethodType(1));
}
#[test]
fn test_try_get_formatted_string_method_type() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_method_type("()V")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Method type ()V", value);
Ok(())
}
#[test]
fn test_add_dynamic() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_dynamic(1, "name", "type")?;
assert_eq!(4, index);
assert_eq!(
Some(&Constant::Dynamic {
bootstrap_method_attr_index: 1,
name_and_type_index: 3
}),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_dynamic() {
test_try_get_constant_tuple(
ConstantPool::try_get_dynamic,
Constant::Dynamic {
bootstrap_method_attr_index: 1,
name_and_type_index: 3,
},
);
}
#[test]
fn test_try_get_formatted_string_dynamic() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let class_index = constant_pool.add_class("Foo")?;
let method_index = constant_pool.add_method_ref(class_index, "x", "()V")?;
let index = constant_pool.add_dynamic(method_index, "x", "()I")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Dynamic #6:x:()I", value);
Ok(())
}
#[test]
fn test_add_invoke_dynamic() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_invoke_dynamic(1, "name", "type")?;
assert_eq!(4, index);
assert_eq!(
Some(&Constant::InvokeDynamic {
bootstrap_method_attr_index: 1,
name_and_type_index: 3
}),
constant_pool.get(index)
);
Ok(())
}
#[test]
fn test_try_get_invoke_dynamic() {
test_try_get_constant_tuple(
ConstantPool::try_get_invoke_dynamic,
Constant::InvokeDynamic {
bootstrap_method_attr_index: 1,
name_and_type_index: 3,
},
);
}
#[test]
fn test_try_get_formatted_string_invoke_dynamic() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let class_index = constant_pool.add_class("Foo")?;
let method_index = constant_pool.add_method_ref(class_index, "x", "()V")?;
let index = constant_pool.add_invoke_dynamic(method_index, "x", "()I")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("InvokeDynamic #6:x:()I", value);
Ok(())
}
#[test]
fn test_add_module() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_module("module")?;
assert_eq!(2, index);
assert_eq!(Some(&Constant::Module(1)), constant_pool.get(index));
Ok(())
}
#[test]
fn test_try_get_module() {
test_try_get_constant(ConstantPool::try_get_module, Constant::Module(1));
}
#[test]
fn test_try_get_formatted_string_module() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_module("foo")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Module foo", value);
Ok(())
}
#[test]
fn test_add_package() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_package("package")?;
assert_eq!(2, index);
assert_eq!(Some(&Constant::Package(1)), constant_pool.get(index));
Ok(())
}
#[test]
fn test_try_get_package() {
test_try_get_constant(ConstantPool::try_get_package, Constant::Package(1));
}
#[test]
fn test_try_get_formatted_string_package() -> Result<()> {
let mut constant_pool = ConstantPool::default();
let index = constant_pool.add_package("foo")?;
let value = constant_pool.try_get_formatted_string(index)?;
assert_eq!("Package foo", value);
Ok(())
}
#[test]
fn test_from_bytes_invalid_tag() {
let data = vec![0, 0, 10];
let mut bytes = ByteReader::new(&data);
assert_eq!(
Err(IoError("Invalid constant pool count".to_string())),
ConstantPool::from_bytes(&mut bytes)
);
}
}