1use 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}