use crate::attributes::Attribute;
use crate::class_file::ClassFile;
use crate::constant::Constant;
use crate::verifiers::error::Result;
use crate::verifiers::error::VerifyError::{
InvalidConstantPoolIndex, InvalidConstantPoolIndexType, VerificationError,
};
pub(crate) fn verify(class_file: &ClassFile<'_>) -> Result<()> {
let mut has_nest_host = false;
let mut has_nest_members = false;
for attribute in &class_file.attributes {
match attribute {
Attribute::NestHost {
host_class_index, ..
} => {
if has_nest_host {
return Err(VerificationError {
context: "NestHost".to_string(),
message: "Multiple NestHost attributes are not allowed".to_string(),
});
}
has_nest_host = true;
if has_nest_members {
return Err(VerificationError {
context: "NestHost".to_string(),
message: "Class cannot have both NestHost and NestMembers attributes"
.to_string(),
});
}
verify_class_index(class_file, *host_class_index, "NestHost.host_class_index")?;
}
Attribute::NestMembers { class_indexes, .. } => {
if has_nest_members {
return Err(VerificationError {
context: "NestMembers".to_string(),
message: "Multiple NestMembers attributes are not allowed".to_string(),
});
}
has_nest_members = true;
if has_nest_host {
return Err(VerificationError {
context: "NestMembers".to_string(),
message: "Class cannot have both NestHost and NestMembers attributes"
.to_string(),
});
}
let mut seen_indexes = std::collections::HashSet::new();
for (i, &class_index) in class_indexes.iter().enumerate() {
verify_class_index(
class_file,
class_index,
&format!("NestMembers.class_indexes[{i}]"),
)?;
if !seen_indexes.insert(class_index) {
return Err(VerificationError {
context: "NestMembers".to_string(),
message: format!("Duplicate class index {class_index} in NestMembers"),
});
}
}
}
_ => {}
}
}
Ok(())
}
fn verify_class_index(class_file: &ClassFile<'_>, index: u16, _context: &str) -> Result<()> {
match class_file.constant_pool.get(index) {
Some(Constant::Class(_)) => Ok(()),
Some(_) => Err(InvalidConstantPoolIndexType(index)),
None => Err(InvalidConstantPoolIndex(index)),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_class_file_with_attributes(attributes: Vec<Attribute>) -> ClassFile<'static> {
ClassFile {
attributes,
..Default::default()
}
}
#[test]
fn test_valid_nest_host() {
let mut class_file = ClassFile::default();
let class_index = class_file.constant_pool.add_class("OuterClass").unwrap();
class_file.attributes.push(Attribute::NestHost {
name_index: 0,
host_class_index: class_index,
});
assert!(verify(&class_file).is_ok());
}
#[test]
fn test_nest_host_invalid_index() {
let class_file = create_class_file_with_attributes(vec![Attribute::NestHost {
name_index: 0,
host_class_index: 9999,
}]);
let result = verify(&class_file);
assert!(matches!(result, Err(InvalidConstantPoolIndex(9999))));
}
#[test]
fn test_nest_host_wrong_index_type() {
let mut class_file = ClassFile::default();
let utf8_index = class_file.constant_pool.add_utf8("NotAClass").unwrap();
class_file.attributes.push(Attribute::NestHost {
name_index: 0,
host_class_index: utf8_index,
});
let result = verify(&class_file);
assert!(matches!(result, Err(InvalidConstantPoolIndexType(_))));
}
#[test]
fn test_multiple_nest_host_attributes() {
let mut class_file = ClassFile::default();
let class_index = class_file.constant_pool.add_class("OuterClass").unwrap();
class_file.attributes.push(Attribute::NestHost {
name_index: 0,
host_class_index: class_index,
});
class_file.attributes.push(Attribute::NestHost {
name_index: 0,
host_class_index: class_index,
});
let result = verify(&class_file);
assert!(result.is_err());
if let Err(VerificationError { message, .. }) = result {
assert!(message.contains("Multiple NestHost"));
}
}
#[test]
fn test_valid_nest_members() {
let mut class_file = ClassFile::default();
let class1 = class_file.constant_pool.add_class("Inner1").unwrap();
let class2 = class_file.constant_pool.add_class("Inner2").unwrap();
class_file.attributes.push(Attribute::NestMembers {
name_index: 0,
class_indexes: vec![class1, class2],
});
assert!(verify(&class_file).is_ok());
}
#[test]
fn test_nest_members_empty() {
let class_file = create_class_file_with_attributes(vec![Attribute::NestMembers {
name_index: 0,
class_indexes: vec![],
}]);
assert!(verify(&class_file).is_ok());
}
#[test]
fn test_nest_members_invalid_index() {
let class_file = create_class_file_with_attributes(vec![Attribute::NestMembers {
name_index: 0,
class_indexes: vec![9999],
}]);
let result = verify(&class_file);
assert!(matches!(result, Err(InvalidConstantPoolIndex(9999))));
}
#[test]
fn test_nest_members_wrong_index_type() {
let mut class_file = ClassFile::default();
let utf8_index = class_file.constant_pool.add_utf8("NotAClass").unwrap();
class_file.attributes.push(Attribute::NestMembers {
name_index: 0,
class_indexes: vec![utf8_index],
});
let result = verify(&class_file);
assert!(matches!(result, Err(InvalidConstantPoolIndexType(_))));
}
#[test]
fn test_nest_members_duplicate_entries() {
let mut class_file = ClassFile::default();
let class_index = class_file.constant_pool.add_class("Inner").unwrap();
class_file.attributes.push(Attribute::NestMembers {
name_index: 0,
class_indexes: vec![class_index, class_index],
});
let result = verify(&class_file);
assert!(result.is_err());
if let Err(VerificationError { message, .. }) = result {
assert!(message.contains("Duplicate"));
}
}
#[test]
fn test_multiple_nest_members_attributes() {
let mut class_file = ClassFile::default();
let class_index = class_file.constant_pool.add_class("Inner").unwrap();
class_file.attributes.push(Attribute::NestMembers {
name_index: 0,
class_indexes: vec![class_index],
});
class_file.attributes.push(Attribute::NestMembers {
name_index: 0,
class_indexes: vec![class_index],
});
let result = verify(&class_file);
assert!(result.is_err());
if let Err(VerificationError { message, .. }) = result {
assert!(message.contains("Multiple NestMembers"));
}
}
#[test]
fn test_nest_host_and_nest_members_conflict_host_first() {
let mut class_file = ClassFile::default();
let class_index = class_file.constant_pool.add_class("SomeClass").unwrap();
class_file.attributes.push(Attribute::NestHost {
name_index: 0,
host_class_index: class_index,
});
class_file.attributes.push(Attribute::NestMembers {
name_index: 0,
class_indexes: vec![class_index],
});
let result = verify(&class_file);
assert!(result.is_err());
if let Err(VerificationError { message, .. }) = result {
assert!(message.contains("both NestHost and NestMembers"));
}
}
#[test]
fn test_nest_host_and_nest_members_conflict_members_first() {
let mut class_file = ClassFile::default();
let class_index = class_file.constant_pool.add_class("SomeClass").unwrap();
class_file.attributes.push(Attribute::NestMembers {
name_index: 0,
class_indexes: vec![class_index],
});
class_file.attributes.push(Attribute::NestHost {
name_index: 0,
host_class_index: class_index,
});
let result = verify(&class_file);
assert!(result.is_err());
if let Err(VerificationError { message, .. }) = result {
assert!(message.contains("both NestHost and NestMembers"));
}
}
#[test]
fn test_no_nest_attributes() {
let class_file = ClassFile::default();
assert!(verify(&class_file).is_ok());
}
}