jvm_assembler/formats/class/reader/
mod.rs1use crate::{
2 formats::class::{view::ClassInfo, ClassReadConfig},
3 program::JvmProgram,
4};
5use byteorder::BigEndian;
6use gaia_types::{BinaryReader, GaiaDiagnostics, GaiaError};
7use std::{
8 cell::{OnceCell, RefCell},
9 io::{Read, Seek},
10};
11
12pub struct ClassReader<'config, R: Read + Seek> {
16 _config: &'config ClassReadConfig,
17 reader: RefCell<BinaryReader<R, BigEndian>>,
18 program: OnceCell<JvmProgram>,
20 info: OnceCell<ClassInfo>,
22}
23
24impl ClassReadConfig {
25 pub fn as_reader<R: Read + Seek>(&self, reader: R) -> ClassReader<'_, R> {
26 ClassReader::new(reader, self)
27 }
28}
29
30impl<'config, R: Read + Seek> ClassReader<'config, R> {
31 pub fn new(reader: R, config: &'config ClassReadConfig) -> Self {
32 Self { _config: config, reader: RefCell::new(BinaryReader::new(reader)), program: Default::default(), info: Default::default() }
33 }
34
35 pub fn get_program(&self) -> Result<&JvmProgram, GaiaError> {
36 self.program.get_or_try_init(|| self.read_program())
37 }
38 pub fn get_info(&self) -> Result<&ClassInfo, GaiaError> {
39 self.info.get_or_try_init(|| self.read_view())
40 }
41}
42
43impl<'config, R: Read + Seek> ClassReader<'config, R> {
44 pub fn read(mut self) -> GaiaDiagnostics<JvmProgram> {
45 match self.get_program() {
46 Ok(_) => {
47 let errors = self.reader.borrow_mut().take_errors();
48 GaiaDiagnostics { result: self.program.take().ok_or(GaiaError::unreachable()), diagnostics: errors }
49 }
50 Err(e) => {
51 let errors = self.reader.borrow_mut().take_errors();
52 GaiaDiagnostics { result: Err(e), diagnostics: errors }
53 }
54 }
55 }
56 fn read_program(&self) -> Result<JvmProgram, GaiaError> {
57 let mut reader = self.reader.borrow_mut();
58
59 reader.seek(std::io::SeekFrom::Start(0))?;
61
62 let (minor_version, major_version) = self.read_class_header(&mut reader)?;
64
65 let constant_pool_entries = self.read_constant_pool(&mut reader)?;
67
68 let (access_flags, this_class, super_class) = self.read_class_info(&mut reader)?;
70
71 let class_name = self.resolve_class_name(&constant_pool_entries, this_class);
73 let super_class_name = if super_class == 0 {
74 None
75 } else {
76 Some(self.resolve_class_name(&constant_pool_entries, super_class))
77 };
78
79 self.skip_interfaces(&mut reader)?;
81
82 let fields = self.read_fields(&mut reader, &constant_pool_entries)?;
84
85 let methods = self.read_methods(&mut reader, &constant_pool_entries)?;
87
88 let mut constant_pool = crate::program::JvmConstantPool::new();
90 for entry in constant_pool_entries.iter() {
91 if let Some(value) = entry {
92 let cp_entry = crate::program::JvmConstantPoolEntry::Utf8 {
93 value: value.clone(),
94 };
95 constant_pool.entries.push(cp_entry);
96 } else {
97 constant_pool.entries.push(crate::program::JvmConstantPoolEntry::Nop);
98 }
99 }
100
101 let mut program = crate::program::JvmProgram::new(class_name);
103 program.version = crate::program::JvmVersion { major: major_version, minor: minor_version };
104 program.access_flags = crate::program::JvmAccessFlags::from_flags(access_flags);
105 program.super_class = super_class_name;
106 program.fields = fields;
107 program.methods = methods;
108 program.constant_pool = constant_pool;
109
110 Ok(program)
111 }
112
113 fn read_class_header(&self, reader: &mut gaia_types::BinaryReader<impl Read + Seek, BigEndian>) -> Result<(u16, u16), GaiaError> {
115 let magic = reader.read_u32()?;
116 if magic != 0xCAFEBABE {
117 return Err(GaiaError::invalid_data("Invalid class file magic number"));
118 }
119
120 let minor_version = reader.read_u16()?;
121 let major_version = reader.read_u16()?;
122
123 Ok((minor_version, major_version))
124 }
125
126 fn read_constant_pool(&self, reader: &mut gaia_types::BinaryReader<impl Read + Seek, BigEndian>) -> Result<Vec<Option<String>>, GaiaError> {
128 let constant_pool_count = reader.read_u16()?;
129 let mut constant_pool_entries: Vec<Option<String>> = vec![None; constant_pool_count as usize];
130
131 let mut i = 1;
133 while i < constant_pool_count {
134 let tag = reader.read_u8()?;
135 match tag {
136 0 => {
137 }
140 1 => {
141 let length = reader.read_u16()?;
143 let mut bytes = vec![0u8; length as usize];
144 reader.read_exact(&mut bytes)?;
145 let utf8_string = String::from_utf8_lossy(&bytes).to_string();
146 constant_pool_entries[i as usize] = Some(utf8_string);
147 }
148 3 => { reader.read_u32()?; }, 4 => { reader.read_u32()?; }, 5 => {
151 reader.read_u64()?;
153 i += 1; },
155 6 => {
156 reader.read_u64()?;
158 i += 1; },
160 7 => {
161 let name_index = reader.read_u16()?;
163 constant_pool_entries[i as usize] = Some(format!("CLASS_REF:{}", name_index));
164 }
165 8 => { reader.read_u16()?; }, 9 | 10 | 11 => {
167 reader.read_u16()?;
169 reader.read_u16()?;
170 }
171 12 => {
172 reader.read_u16()?;
174 reader.read_u16()?;
175 }
176 15 => {
177 reader.read_u8()?;
179 reader.read_u16()?;
180 }
181 16 => { reader.read_u16()?; }, 17 | 18 => {
183 reader.read_u16()?;
185 reader.read_u16()?;
186 }
187 19 | 20 => { reader.read_u16()?; }, _ => {
189 return Err(GaiaError::invalid_data(&format!("Unknown constant pool tag: {}", tag)));
190 }
191 };
192 i += 1;
193 }
194
195 Ok(constant_pool_entries)
196 }
197
198 fn read_class_info(&self, reader: &mut gaia_types::BinaryReader<impl Read + Seek, BigEndian>) -> Result<(u16, u16, u16), GaiaError> {
200 let access_flags = reader.read_u16()?;
201 let this_class = reader.read_u16()?;
202 let super_class = reader.read_u16()?;
203
204 Ok((access_flags, this_class, super_class))
205 }
206
207 fn resolve_class_name(&self, constant_pool_entries: &[Option<String>], this_class: u16) -> String {
209 if let Some(Some(class_ref)) = constant_pool_entries.get(this_class as usize) {
210 if let Some(class_ref_str) = class_ref.strip_prefix("CLASS_REF:") {
211 if let Ok(name_index) = class_ref_str.parse::<u16>() {
212 if let Some(Some(name)) = constant_pool_entries.get(name_index as usize) {
213 return name.clone();
214 }
215 }
216 }
217 }
218 "UnknownClass".to_string()
219 }
220
221 fn skip_interfaces(&self, reader: &mut gaia_types::BinaryReader<impl Read + Seek, BigEndian>) -> Result<(), GaiaError> {
223 let interfaces_count = reader.read_u16()?;
224 for _ in 0..interfaces_count {
225 reader.read_u16()?;
226 }
227 Ok(())
228 }
229
230 fn read_fields(&self, reader: &mut gaia_types::BinaryReader<impl Read + Seek, BigEndian>, constant_pool_entries: &[Option<String>]) -> Result<Vec<crate::program::JvmField>, GaiaError> {
232 let fields_count = reader.read_u16()?;
233 let mut fields = Vec::new();
234
235 for _ in 0..fields_count {
236 let field_access_flags = reader.read_u16()?;
237 let name_index = reader.read_u16()?;
238 let descriptor_index = reader.read_u16()?;
239
240 let field_name = self.get_string_from_pool(constant_pool_entries, name_index, "UnknownField");
242 let field_descriptor = self.get_string_from_pool(constant_pool_entries, descriptor_index, "UnknownDescriptor");
243
244 let mut field = crate::program::JvmField::new(field_name, field_descriptor);
245 field.access_flags = crate::program::JvmAccessFlags::from_flags(field_access_flags);
246
247 self.skip_attributes(reader)?;
249
250 fields.push(field);
251 }
252
253 Ok(fields)
254 }
255
256 fn read_methods(&self, reader: &mut gaia_types::BinaryReader<impl Read + Seek, BigEndian>, constant_pool_entries: &[Option<String>]) -> Result<Vec<crate::program::JvmMethod>, GaiaError> {
258 let methods_count = reader.read_u16()?;
259 let mut methods = Vec::new();
260
261 for _ in 0..methods_count {
262 let method_access_flags = reader.read_u16()?;
263 let name_index = reader.read_u16()?;
264 let descriptor_index = reader.read_u16()?;
265
266 let method_name = self.get_string_from_pool(constant_pool_entries, name_index, "UnknownMethod");
268 let method_descriptor = self.get_string_from_pool(constant_pool_entries, descriptor_index, "UnknownDescriptor");
269
270 let mut method = crate::program::JvmMethod::new(method_name, method_descriptor);
271 method.access_flags = crate::program::JvmAccessFlags::from_flags(method_access_flags);
272
273 self.skip_attributes(reader)?;
275
276 methods.push(method);
277 }
278
279 Ok(methods)
280 }
281
282 fn get_string_from_pool(&self, constant_pool_entries: &[Option<String>], index: u16, default: &str) -> String {
284 constant_pool_entries.get(index as usize)
285 .and_then(|opt| opt.as_ref())
286 .unwrap_or(&default.to_string())
287 .clone()
288 }
289
290 fn skip_attributes(&self, reader: &mut gaia_types::BinaryReader<impl Read + Seek, BigEndian>) -> Result<(), GaiaError> {
292 let attributes_count = reader.read_u16()?;
293 for _ in 0..attributes_count {
294 reader.read_u16()?; let attribute_length = reader.read_u32()?;
296 let mut attribute_data = vec![0u8; attribute_length as usize];
297 reader.read_exact(&mut attribute_data)?;
298 }
299 Ok(())
300 }
301
302 fn read_view(&self) -> Result<ClassInfo, GaiaError> {
303 let mut reader = self.reader.borrow_mut();
304
305 reader.seek(std::io::SeekFrom::Start(0))?;
307
308 let (minor_version, major_version) = self.read_class_header(&mut reader)?;
310
311 let constant_pool_entries = self.read_constant_pool(&mut reader)?;
313
314 let (access_flags, this_class, super_class) = self.read_class_info(&mut reader)?;
316
317 let class_name = self.resolve_class_name(&constant_pool_entries, this_class);
319 let super_class_name = if super_class == 0 {
320 None
321 } else {
322 Some(self.resolve_class_name(&constant_pool_entries, super_class))
323 };
324
325 Ok(ClassInfo {
326 magic: 0xCAFEBABE,
327 version: crate::program::JvmVersion { major: major_version, minor: minor_version },
328 access_flags: crate::program::JvmAccessFlags::from_flags(access_flags),
329 this_class: class_name,
330 super_class: super_class_name,
331 })
332 }
333}