Skip to main content

jvm_assembler/formats/class/writer/
mod.rs

1#![doc = include_str!("readme.md")]
2
3mod attributes;
4mod cp;
5mod entities;
6mod instructions;
7mod pool;
8mod utils;
9
10use crate::program::*;
11use cp::CpEntry;
12use gaia_binary::{BigEndian, BinaryWriter, Fixed};
13use gaia_types::{GaiaDiagnostics, Result};
14use std::{collections::HashMap, io::Write};
15
16/// JVM Class file writer
17pub struct ClassWriter<W: Write> {
18    /// Binary writer
19    writer: BinaryWriter<W, Fixed<BigEndian>>,
20    /// Constant pool entries
21    cp_entries: Vec<CpEntry>,
22    /// Constant pool lookup table
23    cp_map: HashMap<CpEntry, u16>,
24}
25
26impl<W: Write> ClassWriter<W> {
27    /// Create a new Class writer
28    pub fn new(writer: W) -> Self {
29        Self { writer: BinaryWriter::new(writer), cp_entries: Vec::new(), cp_map: HashMap::new() }
30    }
31
32    /// Finish writing and return the underlying writer
33    pub fn finish(self) -> W {
34        self.writer.into_inner()
35    }
36
37    /// Write ClassView as binary Class format
38    pub fn write(mut self, program: &JvmProgram) -> GaiaDiagnostics<W> {
39        match self.write_class_file(program) {
40            Ok(_) => GaiaDiagnostics::success(self.finish()),
41            Err(error) => GaiaDiagnostics::failure(error),
42        }
43    }
44
45    /// Write Class file
46    fn write_class_file(&mut self, program: &JvmProgram) -> Result<()> {
47        // 1. Pre-collect all constants
48        let this_class_idx = self.add_class(program.name.clone());
49        let super_class_idx = if let Some(super_name) = &program.super_class {
50            self.add_class(super_name.clone())
51        }
52        else {
53            self.add_class("java/lang/Object".to_string())
54        };
55
56        // Collect class attribute constants
57        for attr in &program.attributes {
58            self.collect_attribute_constants(attr);
59        }
60
61        // Collect field constants
62        for field in &program.fields {
63            self.add_utf8(field.name.clone());
64            self.add_utf8(field.descriptor.clone());
65            for attr in &field.attributes {
66                self.collect_attribute_constants(attr);
67            }
68            if field.constant_value.is_some() {
69                self.add_utf8("ConstantValue".to_string());
70            }
71        }
72
73        // Collect method constants
74        let code_utf8_idx = self.add_utf8("Code".to_string());
75        for method in &program.methods {
76            self.add_utf8(method.name.clone());
77            self.add_utf8(method.descriptor.clone());
78
79            for handler in &method.exception_handlers {
80                if let Some(catch_type) = &handler.catch_type {
81                    self.add_class(catch_type.clone());
82                }
83            }
84
85            for attr in &method.attributes {
86                self.collect_attribute_constants(attr);
87            }
88            if !method.exceptions.is_empty() {
89                self.add_utf8("Exceptions".to_string());
90            }
91
92            // Pre-generate bytecode to get label positions for StackMapTable generation
93            let (_, label_positions) = self.generate_method_bytecode(method);
94
95            if program.version.major >= 50 {
96                self.add_utf8("StackMapTable".to_string());
97                // If automatic StackMapTable generation is needed, pre-collect its constants
98                let has_stack_map = method.attributes.iter().any(|a| matches!(a, JvmAttribute::StackMapTable { .. }));
99                if !has_stack_map {
100                    let analyzer = crate::analyzer::StackMapAnalyzer::new(program.name.clone(), method, &label_positions);
101                    let frames = analyzer.analyze();
102                    for frame in &frames {
103                        match frame {
104                            JvmStackMapFrame::SameLocals1StackItem { stack, .. }
105                            | JvmStackMapFrame::SameLocals1StackItemExtended { stack, .. } => {
106                                self.collect_verification_type_constants(stack);
107                            }
108                            JvmStackMapFrame::Append { locals, .. } => {
109                                for vt in locals {
110                                    self.collect_verification_type_constants(vt);
111                                }
112                            }
113                            JvmStackMapFrame::Full { locals, stack, .. } => {
114                                for vt in locals {
115                                    self.collect_verification_type_constants(vt);
116                                }
117                                for vt in stack {
118                                    self.collect_verification_type_constants(vt);
119                                }
120                            }
121                            _ => {}
122                        }
123                    }
124                }
125            }
126
127            // Collect constants in instructions
128            for inst in &method.instructions {
129                match inst {
130                    JvmInstruction::Ldc { symbol } | JvmInstruction::LdcW { symbol } | JvmInstruction::Ldc2W { symbol } => {
131                        self.add_string(symbol.clone());
132                    }
133                    JvmInstruction::Getstatic { class_name, field_name, descriptor }
134                    | JvmInstruction::Putstatic { class_name, field_name, descriptor }
135                    | JvmInstruction::Getfield { class_name, field_name, descriptor }
136                    | JvmInstruction::Putfield { class_name, field_name, descriptor } => {
137                        self.add_field_ref(class_name.clone(), field_name.clone(), descriptor.clone());
138                    }
139                    JvmInstruction::Invokevirtual { class_name, method_name, descriptor }
140                    | JvmInstruction::Invokespecial { class_name, method_name, descriptor }
141                    | JvmInstruction::Invokestatic { class_name, method_name, descriptor }
142                    | JvmInstruction::Invokedynamic { class_name, method_name, descriptor } => {
143                        self.add_method_ref(class_name.clone(), method_name.clone(), descriptor.clone());
144                    }
145                    JvmInstruction::Invokeinterface { class_name, method_name, descriptor } => {
146                        self.add_interface_method_ref(class_name.clone(), method_name.clone(), descriptor.clone());
147                    }
148                    JvmInstruction::New { class_name }
149                    | JvmInstruction::Anewarray { class_name }
150                    | JvmInstruction::Checkcast { class_name }
151                    | JvmInstruction::Instanceof { class_name }
152                    | JvmInstruction::Multianewarray { class_name, .. } => {
153                        self.add_class(class_name.clone());
154                    }
155                    _ => {}
156                }
157            }
158        }
159
160        // 2. Start writing
161        // Write magic number
162        self.writer.write_u32(0xCAFEBABE)?;
163
164        // Write version information
165        self.writer.write_u16(program.version.minor)?;
166        self.writer.write_u16(program.version.major)?;
167
168        // Write constant pool
169        self.write_constant_pool()?;
170
171        // Write access flags
172        self.writer.write_u16(program.access_flags.to_flags())?;
173
174        // Write this_class index
175        self.writer.write_u16(this_class_idx)?;
176
177        // Write super_class index
178        self.writer.write_u16(super_class_idx)?;
179
180        // Write interface count (currently 0)
181        self.writer.write_u16(0)?;
182
183        // Write fields
184        self.write_fields(program)?;
185
186        // Write methods
187        self.write_methods(program, code_utf8_idx)?;
188
189        // Write class attributes
190        self.writer.write_u16(program.attributes.len() as u16)?;
191        for attr in &program.attributes {
192            self.write_attribute(attr)?;
193        }
194
195        Ok(())
196    }
197}