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