Skip to main content

lvm_class/
lib.rs

1//! Complete Java 25 `.class` file parser.
2//!
3//! This crate provides structured representation of Java class files with parsing, validation.
4
5use crate::attribute::ClassAttribute;
6use crate::constant_pool::{ConstantEntry, ConstantPool};
7use crate::flags::ClassFlags;
8use crate::member::{FieldInfo, MethodInfo};
9use common::error::ClassFormatErr;
10use common::utils::cursor::ByteCursor;
11
12pub mod attribute;
13pub mod bytecode;
14pub mod constant_pool;
15pub mod flags;
16pub mod member;
17pub mod prelude;
18#[cfg(feature = "write")]
19pub mod write;
20
21// TODO: review all access levels in the crate (methods, fields, modules, structs, etc.)
22
23/// A Rust representation of a Java `.class` file.
24///
25/// https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html
26///
27/// All structures in the crate have public fields for easier access,
28/// because they will be remapped to runtime structures.
29#[derive(Debug)]
30pub struct ClassFile {
31    pub minor_version: u16,
32    pub major_version: u16,
33    pub cp: ConstantPool,
34    pub access_flags: ClassFlags,
35    pub this_class: u16,
36    pub super_class: u16,
37    pub interfaces: Vec<u16>,
38    pub fields: Vec<FieldInfo>,
39    pub methods: Vec<MethodInfo>,
40    pub attributes: Vec<ClassAttribute>,
41}
42
43impl ClassFile {
44    const MAGIC: u32 = 0xCAFEBABE;
45    fn validate_magic(val: u32) -> Result<(), ClassFormatErr> {
46        (val == ClassFile::MAGIC)
47            .then_some(())
48            .ok_or(ClassFormatErr::WrongMagic(val))
49    }
50
51    pub fn get_super_class_name(&self) -> Option<Result<&str, ClassFormatErr>> {
52        if self.super_class == 0 {
53            None
54        } else {
55            Some(self.cp.get_class_name(&self.super_class))
56        }
57    }
58}
59
60impl TryFrom<Vec<u8>> for ClassFile {
61    type Error = ClassFormatErr;
62
63    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
64        let mut cursor = ByteCursor::new(&value);
65        let magic = cursor.u32()?;
66        ClassFile::validate_magic(magic)?;
67        let minor_version = cursor.u16()?;
68        let major_version = cursor.u16()?;
69        let constant_pool_count = cursor.u16()?;
70        let mut constant_pool = Vec::with_capacity((constant_pool_count + 1) as usize);
71        constant_pool.push(ConstantEntry::Unused);
72        let mut i = 1;
73        while i < constant_pool_count {
74            let constant = ConstantEntry::read(&mut cursor)?;
75            constant_pool.push(constant.clone());
76            match constant {
77                // described in JVM spec that Long and Double take two entries in the constant pool
78                // https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.4.5
79                ConstantEntry::Long(_) | ConstantEntry::Double(_) => {
80                    constant_pool.push(ConstantEntry::Unused);
81                    i += 2;
82                }
83                _ => {
84                    i += 1;
85                }
86            }
87        }
88        let constant_pool = ConstantPool {
89            inner: constant_pool,
90        };
91
92        let access_flags = ClassFlags::new(cursor.u16()?);
93        let this_class = cursor.u16()?;
94        let super_class = cursor.u16()?;
95        let interfaces_count = cursor.u16()?;
96        let mut interfaces = Vec::with_capacity(interfaces_count as usize);
97        for _ in 0..interfaces_count {
98            interfaces.push(cursor.u16()?);
99        }
100        let fields_count = cursor.u16()?;
101        let mut fields = Vec::with_capacity(fields_count as usize);
102        for _ in 0..fields_count {
103            fields.push(FieldInfo::read(&constant_pool, &mut cursor)?);
104        }
105        let methods_count = cursor.u16()?;
106        let mut methods = Vec::with_capacity(methods_count as usize);
107        for _ in 0..methods_count {
108            methods.push(MethodInfo::read(&constant_pool, &mut cursor)?);
109        }
110        let attributes_count = cursor.u16()?;
111        let mut attributes = Vec::with_capacity(attributes_count as usize);
112        for _ in 0..attributes_count {
113            attributes.push(ClassAttribute::read(&constant_pool, &mut cursor)?);
114        }
115
116        if cursor.u8().is_ok() {
117            Err(ClassFormatErr::TrailingBytes)
118        } else {
119            Ok(Self {
120                minor_version,
121                major_version,
122                cp: constant_pool,
123                access_flags,
124                this_class,
125                super_class,
126                interfaces,
127                fields,
128                methods,
129                attributes,
130            })
131        }
132    }
133}