class_rs/
lib.rs

1//! JVM class file reader
2//!
3//! Reads a .class file into an almost 1-to-1 matching struct or generates a .class file from said structure.
4
5use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
6use std::error::Error;
7use std::io::{Read, Seek, Write};
8
9mod enums;
10pub use enums::{
11    AccessFlag, Attribute, Constant, ElementValue, Instruction, StackMapFrameType, TargetInfo,
12    VerificationType,
13};
14
15mod structs;
16pub use structs::{
17    Annotation, BootstrapMethod, ElementValuePair, Field, InnerClass, LineNumber, LocalVar,
18    LocalVariable, LocalVariableType, LookupSwitchPair, MemberData, Method, MethodParameter,
19    ModuleExports, ModuleOpens, ModuleProvides, ModuleRequires, StackMapFrame, TypeAnnotation,
20    TypePath,
21};
22
23mod reader;
24use crate::reader::{
25    extract_class_flags, read_attributes, read_constant_pool, read_fields, read_interfaces,
26    read_methods,
27};
28
29mod writer;
30use crate::writer::{
31    compact_class_flags, write_attributes, write_constant_pool, write_fields, write_interfaces,
32    write_methods,
33};
34
35mod errors;
36pub use errors::JavaError;
37
38mod mapping;
39
40#[derive(Debug)]
41pub struct JVMClass {
42    pub major: u16,
43    pub minor: u16,
44    pub access_flags: Vec<AccessFlag>,
45    pub this_class: u16,
46    pub super_class: u16,
47    pub constants: Vec<Constant>,
48    pub interfaces: Vec<u16>,
49    pub fields: Vec<Field>,
50    pub methods: Vec<Method>,
51    pub attributes: Vec<Attribute>,
52}
53
54impl JVMClass {
55    pub fn new() -> Self {
56        Self {
57            major: 0,
58            minor: 0,
59            access_flags: vec![],
60            this_class: 0,
61            super_class: 0,
62            constants: vec![],
63            interfaces: vec![],
64            fields: vec![],
65            methods: vec![],
66            attributes: vec![],
67        }
68    }
69
70    pub fn load<R: Read>(&mut self, r: &mut R) -> Result<(), Box<dyn Error>> {
71        let magic = r.read_u32::<BigEndian>()?;
72        assert_eq!(magic, 0xCAFEBABE);
73
74        self.minor = r.read_u16::<BigEndian>()?;
75        self.major = r.read_u16::<BigEndian>()?;
76
77        self.constants = read_constant_pool(r)?;
78
79        let access_flags = r.read_u16::<BigEndian>()?;
80        self.access_flags = extract_class_flags(access_flags);
81
82        self.this_class = r.read_u16::<BigEndian>()?;
83        self.super_class = r.read_u16::<BigEndian>()?;
84
85        self.interfaces = read_interfaces(r)?;
86        self.fields = read_fields(&self, r)?;
87        self.methods = read_methods(&self, r)?;
88        self.attributes = read_attributes(&self, r)?;
89
90        Ok(())
91    }
92
93    pub fn store<W: Write + Seek>(&self, w: &mut W) -> Result<(), Box<dyn Error>> {
94        w.write_u32::<BigEndian>(0xCAFEBABE)?;
95
96        w.write_u16::<BigEndian>(self.minor)?;
97        w.write_u16::<BigEndian>(self.major)?;
98
99        write_constant_pool(w, &self.constants)?;
100
101        let access_flags = compact_class_flags(&self.access_flags);
102        w.write_u16::<BigEndian>(access_flags)?;
103
104        w.write_u16::<BigEndian>(self.this_class)?;
105        w.write_u16::<BigEndian>(self.super_class)?;
106
107        write_interfaces(w, &self.interfaces)?;
108        write_fields(w, &self.fields, self)?;
109        write_methods(w, &self.methods, self)?;
110        write_attributes(w, &self.attributes, self)?;
111
112        Ok(())
113    }
114
115    pub fn get_string(&self, id: u16) -> Result<&str, JavaError> {
116        let id = id as usize;
117
118        if let Some(constant) = self.constants.get(id) {
119            match constant {
120                Constant::Class { name_index } => self.get_string(*name_index),
121                Constant::Utf8(string) => Ok(string),
122                Constant::String { string_index } => self.get_string(*string_index),
123                _ => Err(JavaError::ConstantTypeError(format!(
124                    "#{id} is not a string, but a {constant}"
125                ))),
126            }
127        } else {
128            Err(JavaError::InvalidConstantId)
129        }
130    }
131
132    pub fn get_string_index(&self, string: &str) -> Result<u16, JavaError> {
133        for (index, constant) in self.constants.iter().enumerate() {
134            if let Constant::Utf8(s) = constant {
135                if s == string {
136                    return Ok(index as u16);
137                }
138            }
139        }
140
141        Err(JavaError::StringNotFound)
142    }
143}