1use crate::attributes::Attribute;
2use crate::byte_reader::ByteReader;
3use crate::class_access_flags::ClassAccessFlags;
4use crate::constant_pool::ConstantPool;
5use crate::display::indent_lines;
6use crate::error::Error::InvalidMagicNumber;
7use crate::error::Result;
8use crate::field::Field;
9use crate::java_string::JavaStr;
10use crate::method::Method;
11use crate::verifiers::verifier;
12use crate::version::Version;
13use byteorder::{BigEndian, WriteBytesExt};
14use std::fmt;
15
16const MAGIC: u32 = 0xCAFE_BABE;
21
22#[derive(Clone, Debug, Default, Eq, PartialEq)]
55pub struct ClassFile<'a> {
56 pub version: Version,
57 pub constant_pool: ConstantPool<'a>,
58 pub access_flags: ClassAccessFlags,
59 pub this_class: u16,
60 pub super_class: u16,
61 pub interfaces: Vec<u16>,
62 pub fields: Vec<Field>,
63 pub methods: Vec<Method>,
64 pub attributes: Vec<Attribute>,
65 pub code_source_url: Option<String>,
68}
69
70impl<'a> ClassFile<'a> {
71 #[must_use]
73 pub fn into_owned(self) -> ClassFile<'static> {
74 ClassFile {
75 version: self.version,
76 constant_pool: self.constant_pool.into_owned(),
77 access_flags: self.access_flags,
78 this_class: self.this_class,
79 super_class: self.super_class,
80 interfaces: self.interfaces,
81 fields: self.fields,
82 methods: self.methods,
83 attributes: self.attributes,
84 code_source_url: self.code_source_url,
85 }
86 }
87
88 pub fn class_name(&self) -> Result<&JavaStr> {
105 self.constant_pool.try_get_class(self.this_class)
106 }
107
108 pub fn verify(&self) -> Result<()> {
131 verifier::verify(self).map_err(crate::Error::from)
132 }
133
134 pub fn from_bytes(bytes: &[u8]) -> Result<ClassFile<'static>> {
173 let mut reader = ByteReader::new(bytes);
174 Self::parse_from_reader(&mut reader)
175 }
176
177 pub fn from_slice(data: &'a [u8]) -> Result<ClassFile<'a>> {
187 let mut reader = ByteReader::new(data);
188 Self::from_byte_reader(&mut reader)
189 }
190
191 fn parse_from_reader(reader: &mut ByteReader<'_>) -> Result<ClassFile<'static>> {
194 let magic = reader.read_u32()?;
195 if magic != MAGIC {
196 return Err(InvalidMagicNumber(magic));
197 }
198
199 let version = Version::from_bytes(reader)?;
200 let constant_pool = ConstantPool::from_bytes(reader)?.into_owned();
201 let access_flags = ClassAccessFlags::from_bits_truncate(reader.read_u16()?);
202 let this_class = reader.read_u16()?;
203 let super_class = reader.read_u16()?;
204
205 let interfaces_count = reader.read_u16()? as usize;
206 let mut interfaces = Vec::with_capacity(interfaces_count);
207 for _ in 0..interfaces_count {
208 interfaces.push(reader.read_u16()?);
209 }
210
211 let field_count = reader.read_u16()? as usize;
212 let mut fields = Vec::with_capacity(field_count);
213 for _ in 0..field_count {
214 let field = Field::from_bytes(&constant_pool, reader)?;
215 fields.push(field);
216 }
217
218 let method_count = reader.read_u16()? as usize;
219 let mut methods = Vec::with_capacity(method_count);
220 for _ in 0..method_count {
221 let method = Method::from_bytes(&constant_pool, reader)?;
222 methods.push(method);
223 }
224
225 let attribute_count = reader.read_u16()? as usize;
226 let mut attributes = Vec::with_capacity(attribute_count);
227 for _ in 0..attribute_count {
228 let attribute = Attribute::from_bytes(&constant_pool, reader)?;
229 attributes.push(attribute);
230 }
231
232 Ok(ClassFile {
233 version,
234 constant_pool,
235 access_flags,
236 this_class,
237 super_class,
238 interfaces,
239 fields,
240 methods,
241 attributes,
242 code_source_url: None,
243 })
244 }
245
246 fn from_byte_reader(reader: &mut ByteReader<'a>) -> Result<ClassFile<'a>> {
248 let magic = reader.read_u32()?;
249 if magic != MAGIC {
250 return Err(InvalidMagicNumber(magic));
251 }
252
253 let version = Version::from_bytes(reader)?;
254 let constant_pool = ConstantPool::from_bytes(reader)?;
255 let access_flags = ClassAccessFlags::from_bits_truncate(reader.read_u16()?);
256 let this_class = reader.read_u16()?;
257 let super_class = reader.read_u16()?;
258
259 let interfaces_count = reader.read_u16()? as usize;
260 let mut interfaces = Vec::with_capacity(interfaces_count);
261 for _ in 0..interfaces_count {
262 interfaces.push(reader.read_u16()?);
263 }
264
265 let field_count = reader.read_u16()? as usize;
266 let mut fields = Vec::with_capacity(field_count);
267 for _ in 0..field_count {
268 let field = Field::from_bytes(&constant_pool, reader)?;
269 fields.push(field);
270 }
271
272 let method_count = reader.read_u16()? as usize;
273 let mut methods = Vec::with_capacity(method_count);
274 for _ in 0..method_count {
275 let method = Method::from_bytes(&constant_pool, reader)?;
276 methods.push(method);
277 }
278
279 let attribute_count = reader.read_u16()? as usize;
280 let mut attributes = Vec::with_capacity(attribute_count);
281 for _ in 0..attribute_count {
282 let attribute = Attribute::from_bytes(&constant_pool, reader)?;
283 attributes.push(attribute);
284 }
285
286 Ok(ClassFile {
287 version,
288 constant_pool,
289 access_flags,
290 this_class,
291 super_class,
292 interfaces,
293 fields,
294 methods,
295 attributes,
296 code_source_url: None,
297 })
298 }
299
300 pub fn to_bytes(&self, bytes: &mut Vec<u8>) -> Result<()> {
343 bytes.write_u32::<BigEndian>(MAGIC)?;
344 self.version.to_bytes(bytes)?;
345 self.constant_pool.to_bytes(bytes)?;
346 self.access_flags.to_bytes(bytes)?;
347 bytes.write_u16::<BigEndian>(self.this_class)?;
348 bytes.write_u16::<BigEndian>(self.super_class)?;
349
350 let interfaces_length = u16::try_from(self.interfaces.len())?;
351 bytes.write_u16::<BigEndian>(interfaces_length)?;
352 for interface in &self.interfaces {
353 bytes.write_u16::<BigEndian>(*interface)?;
354 }
355
356 let fields_length = u16::try_from(self.fields.len())?;
357 bytes.write_u16::<BigEndian>(fields_length)?;
358 for field in &self.fields {
359 field.to_bytes(bytes)?;
360 }
361
362 let methods_length = u16::try_from(self.methods.len())?;
363 bytes.write_u16::<BigEndian>(methods_length)?;
364 for method in &self.methods {
365 method.to_bytes(bytes)?;
366 }
367
368 let attributes_length = u16::try_from(self.attributes.len())?;
369 bytes.write_u16::<BigEndian>(attributes_length)?;
370 for attribute in &self.attributes {
371 attribute.to_bytes(bytes)?;
372 }
373
374 Ok(())
375 }
376}
377
378impl fmt::Display for ClassFile<'_> {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 let class_name = self.class_name().map_err(|_| fmt::Error)?;
424 writeln!(f, "{} {class_name}", self.access_flags.as_code())?;
425 writeln!(f, " minor version: {}", self.version.minor())?;
426 writeln!(
427 f,
428 " major version: {} ({})",
429 self.version.major(),
430 self.version
431 )?;
432 writeln!(f, " flags: {}", self.access_flags)?;
433 writeln!(f, " this_class: #{}", self.this_class)?;
434 writeln!(f, " super_class: #{}", self.super_class)?;
435 writeln!(
436 f,
437 " interfaces: {}, fields: {}, methods: {}, attributes: {}",
438 self.interfaces.len(),
439 self.fields.len(),
440 self.methods.len(),
441 self.attributes.len()
442 )?;
443
444 writeln!(f, "Constant pool:")?;
445 write!(f, "{}", self.constant_pool)?;
446 writeln!(f, "{{")?;
447
448 if !self.interfaces.is_empty() {
449 writeln!(f, "interfaces:")?;
450 for interface in &self.interfaces {
451 writeln!(f, " #{interface}")?;
452 }
453 }
454
455 if !self.fields.is_empty() {
456 writeln!(f, "fields:")?;
457 for (index, field) in self.fields.iter().enumerate() {
458 if index > 0 {
459 writeln!(f)?;
460 }
461 writeln!(f, "{}", indent_lines(&field.to_string(), " "))?;
462 }
463 }
464
465 writeln!(f, "methods:")?;
466 for (index, method) in self.methods.iter().enumerate() {
467 if index > 0 {
468 writeln!(f)?;
469 }
470 writeln!(f, "{}", indent_lines(&method.to_string(), " "))?;
471 }
472
473 writeln!(f, "}}")?;
474
475 for attribute in &self.attributes {
476 writeln!(f, "{}", &attribute.to_string())?;
477 }
478 Ok(())
479 }
480}
481
482#[cfg(test)]
483mod test {
484 use super::*;
485 use crate::Error::{InvalidConstantPoolIndexType, IoError};
486 use crate::error::Result;
487 use crate::{Constant, JAVA_8, JAVA_21};
488 use indoc::indoc;
489
490 #[test]
491 fn test_invalid_magic() {
492 let invalid_magic: u32 = 0x0102_0304;
493 let bytes = invalid_magic.to_be_bytes();
494 assert_eq!(
495 Err(InvalidMagicNumber(invalid_magic)),
496 ClassFile::from_bytes(&bytes)
497 );
498 }
499
500 #[test]
501 fn test_class_name() -> Result<()> {
502 let class_bytes = include_bytes!("../../classes/Minimum.class");
503 let class_file = ClassFile::from_bytes(class_bytes.as_slice())?;
504 assert_eq!("Minimum", class_file.class_name()?);
505 Ok(())
506 }
507
508 #[test]
509 fn test_class_name_invalid_constant_pool() -> Result<()> {
510 let mut constant_pool = ConstantPool::default();
511 let utf8_index = constant_pool.add_utf8("Test")?;
512 let class_file = ClassFile {
513 constant_pool,
514 this_class: utf8_index,
515 ..Default::default()
516 };
517 assert_eq!(
518 Err(InvalidConstantPoolIndexType(1)),
519 class_file.class_name()
520 );
521 Ok(())
522 }
523
524 #[test]
525 fn test_verify() -> Result<()> {
526 let class_bytes = include_bytes!("../../classes/Minimum.class");
527 let class_file = ClassFile::from_bytes(class_bytes.as_slice())?;
528 assert!(class_file.verify().is_ok());
529 Ok(())
530 }
531
532 #[test]
533 fn test_verify_error() -> Result<()> {
534 let mut constant_pool = ConstantPool::default();
535 let this_class = constant_pool.add_class("Test")?;
536 constant_pool.push(Constant::Class(u16::MAX));
538 let class_file = ClassFile {
539 version: JAVA_21,
540 constant_pool: constant_pool.clone(),
541 this_class,
542 ..Default::default()
543 };
544
545 assert_eq!(
546 Err(crate::Error::VerificationError(
547 crate::verifiers::error::VerifyError::InvalidConstantPoolIndex(3)
548 )),
549 class_file.verify()
550 );
551 Ok(())
552 }
553
554 #[test]
555 fn test_minimum_to_string() -> Result<()> {
556 let class_bytes = include_bytes!("../../classes/Minimum.class");
557 let class_file = ClassFile::from_bytes(class_bytes.as_slice())?;
558 let expected = indoc! {r"
559 public class Minimum
560 minor version: 0
561 major version: 52 (Java 8)
562 flags: (0x0021) ACC_PUBLIC, ACC_SUPER
563 this_class: #7
564 super_class: #2
565 interfaces: 0, fields: 0, methods: 1, attributes: 1
566 Constant pool:
567 #1 = Methodref #2.#3
568 #2 = Class #4
569 #3 = NameAndType #5:#6
570 #4 = Utf8 java/lang/Object
571 #5 = Utf8 <init>
572 #6 = Utf8 ()V
573 #7 = Class #8
574 #8 = Utf8 Minimum
575 #9 = Utf8 Code
576 #10 = Utf8 LineNumberTable
577 #11 = Utf8 SourceFile
578 #12 = Utf8 Minimum.java
579 {
580 methods:
581 flags: (0x0001) ACC_PUBLIC
582 name_index: #5
583 descriptor_index: #6
584 attributes:
585 Code:
586 stack=1, locals=1
587 0: aload_0
588 1: invokespecial #1
589 4: return
590 LineNumberTable:
591 line 1: 0
592 }
593 SourceFile { name_index: 11, source_file_index: 12 }
594 "};
595
596 assert_eq!(expected, class_file.to_string());
597 Ok(())
598 }
599
600 #[test]
601 fn test_minimum_serialization() -> Result<()> {
602 let class_bytes = include_bytes!("../../classes/Minimum.class");
603 let expected_bytes = class_bytes.to_vec();
604 let class_file = ClassFile::from_bytes(&expected_bytes)?;
605
606 assert_eq!(JAVA_8, class_file.version);
607 assert_eq!(
608 ClassAccessFlags::PUBLIC | ClassAccessFlags::SUPER,
609 class_file.access_flags
610 );
611 assert_eq!("Minimum", class_file.class_name()?);
612
613 let mut bytes = Vec::with_capacity(4);
614 class_file.to_bytes(&mut bytes)?;
615 assert_eq!(expected_bytes, bytes);
616 Ok(())
617 }
618
619 #[test]
620 fn test_from_bytes_invalid() {
621 let bytes = vec![
622 202, 254, 186, 190, 254, 0, 0, 48, 0, 0, 160, 93, 37, 0, 212, 186,
623 ];
624 assert_eq!(
625 Err(IoError("Invalid constant pool count".to_string())),
626 ClassFile::from_bytes(&bytes)
627 );
628 }
629}