#![allow(dead_code)]
use std::{
fs,
path::{Path, PathBuf},
};
pub fn fixture_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/classes")
}
pub fn fixture(name: &str) -> Vec<u8> {
let path = fixture_dir().join(name);
fs::read(&path).unwrap_or_else(|error| {
panic!(
"cannot read {}: {error}\nrun tests/fixtures/build.sh to rebuild the fixtures",
path.display()
)
})
}
pub fn all_fixtures() -> Vec<(String, Vec<u8>)> {
let root = fixture_dir();
let mut classes = Vec::new();
let mut pending = vec![root.clone()];
while let Some(directory) = pending.pop() {
let entries = fs::read_dir(&directory)
.unwrap_or_else(|error| panic!("cannot list {}: {error}", directory.display()));
for entry in entries {
let path = entry.expect("cannot read directory entry").path();
if path.is_dir() {
pending.push(path);
} else if path.extension().is_some_and(|ext| ext == "class") {
let name = path
.strip_prefix(&root)
.expect("fixture outside of the fixture directory")
.to_string_lossy()
.into_owned();
classes.push((name, fs::read(&path).expect("cannot read fixture")));
}
}
}
classes.sort_by(|(left, _), (right, _)| left.cmp(right));
classes
}
pub const JAVA_8: u16 = 52;
pub struct ClassBuilder {
pub magic: u32,
pub minor_version: u16,
pub major_version: u16,
pub constant_pool_count: Option<u16>,
pub access_flags: u16,
pub this_class: u16,
pub super_class: u16,
pub interfaces: Vec<u16>,
pool: Vec<u8>,
slots: u16,
fields: Table,
methods: Table,
attributes: Table,
}
impl ClassBuilder {
pub fn new() -> Self {
let mut builder = Self {
magic: 0xCAFE_BABE,
minor_version: 0,
major_version: JAVA_8,
constant_pool_count: None,
access_flags: 0x0021,
this_class: 0,
super_class: 0,
interfaces: Vec::new(),
pool: Vec::new(),
slots: 1,
fields: Table::new(),
methods: Table::new(),
attributes: Table::new(),
};
builder.this_class = builder.class_entry("Generated");
builder.super_class = builder.class_entry("java/lang/Object");
builder
}
pub fn raw_entry(&mut self, bytes: &[u8], slots: u16) -> u16 {
let index = self.slots;
self.pool.extend_from_slice(bytes);
self.slots += slots;
index
}
pub fn utf8_entry(&mut self, value: &str) -> u16 {
self.utf8_bytes_entry(value.as_bytes())
}
pub fn utf8_bytes_entry(&mut self, bytes: &[u8]) -> u16 {
let mut entry = vec![1];
push_u16(&mut entry, bytes.len() as u16);
entry.extend_from_slice(bytes);
self.raw_entry(&entry, 1)
}
pub fn integer_entry(&mut self, value: i32) -> u16 {
let mut entry = vec![3];
entry.extend_from_slice(&value.to_be_bytes());
self.raw_entry(&entry, 1)
}
pub fn float_entry(&mut self, value: f32) -> u16 {
let mut entry = vec![4];
entry.extend_from_slice(&value.to_bits().to_be_bytes());
self.raw_entry(&entry, 1)
}
pub fn long_entry(&mut self, value: i64) -> u16 {
let mut entry = vec![5];
entry.extend_from_slice(&value.to_be_bytes());
self.raw_entry(&entry, 2)
}
pub fn double_entry(&mut self, value: f64) -> u16 {
let mut entry = vec![6];
entry.extend_from_slice(&value.to_bits().to_be_bytes());
self.raw_entry(&entry, 2)
}
pub fn class_entry(&mut self, name: &str) -> u16 {
let name_index = self.utf8_entry(name);
self.reference_entry(7, name_index, None)
}
pub fn string_entry(&mut self, value: &str) -> u16 {
let value_index = self.utf8_entry(value);
self.reference_entry(8, value_index, None)
}
pub fn name_and_type_entry(&mut self, name: &str, descriptor: &str) -> u16 {
let name_index = self.utf8_entry(name);
let descriptor_index = self.utf8_entry(descriptor);
self.reference_entry(12, name_index, Some(descriptor_index))
}
pub fn method_ref_entry(&mut self, class: &str, name: &str, descriptor: &str) -> u16 {
let class_index = self.class_entry(class);
let name_and_type_index = self.name_and_type_entry(name, descriptor);
self.reference_entry(10, class_index, Some(name_and_type_index))
}
pub fn method_handle_entry(&mut self, reference_kind: u8, reference_index: u16) -> u16 {
let mut entry = vec![15, reference_kind];
push_u16(&mut entry, reference_index);
self.raw_entry(&entry, 1)
}
pub fn method_type_entry(&mut self, descriptor: &str) -> u16 {
let descriptor_index = self.utf8_entry(descriptor);
self.reference_entry(16, descriptor_index, None)
}
pub fn reference_entry(&mut self, tag: u8, first: u16, second: Option<u16>) -> u16 {
let mut entry = vec![tag];
push_u16(&mut entry, first);
if let Some(second) = second {
push_u16(&mut entry, second);
}
self.raw_entry(&entry, 1)
}
pub fn attribute(&mut self, name: &str, body: &[u8]) -> Vec<u8> {
let name_index = self.utf8_entry(name);
raw_attribute(name_index, body.len() as u32, body)
}
pub fn broken_attribute(&mut self, name: &str, declared: u32, body: &[u8]) -> Vec<u8> {
let name_index = self.utf8_entry(name);
raw_attribute(name_index, declared, body)
}
pub fn field(&mut self, access_flags: u16, name: &str, descriptor: &str, attrs: &[Vec<u8>]) {
let member = self.member(access_flags, name, descriptor, attrs);
self.fields.push(&member);
}
pub fn method(&mut self, access_flags: u16, name: &str, descriptor: &str, attrs: &[Vec<u8>]) {
let member = self.member(access_flags, name, descriptor, attrs);
self.methods.push(&member);
}
pub fn class_attribute(&mut self, attribute: &[u8]) {
self.attributes.push(attribute);
}
fn member(
&mut self,
access_flags: u16,
name: &str,
descriptor: &str,
attrs: &[Vec<u8>],
) -> Vec<u8> {
let name_index = self.utf8_entry(name);
let descriptor_index = self.utf8_entry(descriptor);
let mut bytes = Vec::new();
push_u16(&mut bytes, access_flags);
push_u16(&mut bytes, name_index);
push_u16(&mut bytes, descriptor_index);
push_u16(&mut bytes, attrs.len() as u16);
for attribute in attrs {
bytes.extend_from_slice(attribute);
}
bytes
}
pub fn build(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.magic.to_be_bytes());
push_u16(&mut bytes, self.minor_version);
push_u16(&mut bytes, self.major_version);
push_u16(&mut bytes, self.constant_pool_count.unwrap_or(self.slots));
bytes.extend_from_slice(&self.pool);
push_u16(&mut bytes, self.access_flags);
push_u16(&mut bytes, self.this_class);
push_u16(&mut bytes, self.super_class);
push_u16(&mut bytes, self.interfaces.len() as u16);
for &interface in &self.interfaces {
push_u16(&mut bytes, interface);
}
for table in [&self.fields, &self.methods, &self.attributes] {
push_u16(&mut bytes, table.count);
bytes.extend_from_slice(&table.bytes);
}
bytes
}
}
struct Table {
count: u16,
bytes: Vec<u8>,
}
impl Table {
fn new() -> Self {
Self {
count: 0,
bytes: Vec::new(),
}
}
fn push(&mut self, entry: &[u8]) {
self.count += 1;
self.bytes.extend_from_slice(entry);
}
}
fn raw_attribute(name_index: u16, declared: u32, body: &[u8]) -> Vec<u8> {
let mut bytes = Vec::new();
push_u16(&mut bytes, name_index);
bytes.extend_from_slice(&declared.to_be_bytes());
bytes.extend_from_slice(body);
bytes
}
pub fn push_u16(bytes: &mut Vec<u8>, value: u16) {
bytes.extend_from_slice(&value.to_be_bytes());
}