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, ExceptionTableEntry, Field, InnerClass,
18    LineNumber, LocalVar, LocalVariable, LocalVariableType, LookupSwitchPair, MemberData, Method,
19    MethodParameter, ModuleExports, ModuleOpens, ModuleProvides, ModuleRequires, StackMapFrame,
20    TypeAnnotation, 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 } => {
121                    let cname = self.get_string(*name_index)?;
122
123                    if cname.starts_with("java/lang/") {
124                        Ok(&cname[10..])
125                    } else {
126                        Ok(cname)
127                    }
128                }
129                Constant::Utf8(string) => Ok(string),
130                Constant::String { string_index } => self.get_string(*string_index),
131                _ => Err(JavaError::ConstantTypeError(format!(
132                    "#{id} is not a string, but a {constant}"
133                ))),
134            }
135        } else {
136            Err(JavaError::InvalidConstantId(id as u16))
137        }
138    }
139
140    pub fn get_string_index(&self, string: &str) -> Result<u16, JavaError> {
141        for (index, constant) in self.constants.iter().enumerate() {
142            if let Constant::Utf8(s) = constant {
143                if s == string {
144                    return Ok(index as u16);
145                }
146            }
147        }
148
149        Err(JavaError::StringNotFound)
150    }
151
152    pub fn get_bootstrap_methods(&self) -> &Vec<BootstrapMethod> {
153        for attr in &self.attributes {
154            match attr {
155                Attribute::BootstrapMethods(bootstrap_methods) => {
156                    return bootstrap_methods;
157                }
158                _ => {}
159            }
160        }
161
162        unreachable!();
163    }
164
165    pub fn get_bootstrap_method(&self, id: u16) -> &BootstrapMethod {
166        let methods = self.get_bootstrap_methods();
167
168        &methods[id as usize]
169    }
170
171    pub fn get_constant(&self, id: &u16) -> &Constant {
172        self.constants.get(*id as usize).unwrap()
173    }
174}