dice_rust/jvm/
java_class_generator.rs

1use super::jvm_types::{ConstantPool, ConstantPoolEntry, JvmInstruction};
2use crate::analyzer::SemanticAnalyzer;
3/// Java class file generator
4use std::fs;
5
6/// Java class generator error
7#[derive(Debug)]
8pub enum JavaClassGeneratorError {
9    CompilationError(String),
10}
11
12impl From<Box<dyn std::error::Error>> for JavaClassGeneratorError {
13    fn from(error: Box<dyn std::error::Error>) -> Self {
14        JavaClassGeneratorError::CompilationError(error.to_string())
15    }
16}
17
18impl From<String> for JavaClassGeneratorError {
19    fn from(error: String) -> Self {
20        JavaClassGeneratorError::CompilationError(error)
21    }
22}
23
24impl std::fmt::Display for JavaClassGeneratorError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            JavaClassGeneratorError::CompilationError(msg) => write!(f, "Compilation error: {msg}"),
28        }
29    }
30}
31
32impl std::error::Error for JavaClassGeneratorError {}
33
34/// Complete Java class file generator
35pub struct JavaClassGenerator {
36    constant_pool: ConstantPool,
37    class_name: String,
38}
39
40impl JavaClassGenerator {
41    pub fn new(class_name: String) -> Self {
42        Self {
43            constant_pool: ConstantPool::new(),
44            class_name,
45        }
46    }
47
48    /// Generate Java class file from Dice expression
49    pub fn generate_dice_class(
50        &mut self,
51        expression: &str,
52    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
53        // AST analysis
54        let mut analyzer = SemanticAnalyzer::new(expression)?;
55        let ast = analyzer.analyze()?;
56
57        if let Some(stmt) = ast.statement {
58            let crate::ast::StatementKind::Expression { expr } = stmt.kind;
59            let crate::ast::ExpressionKind::Dice { count, faces } = expr.kind;
60
61            self.setup_constant_pool()
62                .map_err(JavaClassGeneratorError::CompilationError)?;
63            let bytecode = self.generate_dice_bytecode(count, faces)?;
64            return self.generate_class_file(bytecode);
65        }
66
67        Err("Invalid expression".into())
68    }
69
70    /// Generate JVM instruction sequence from Dice expression (for VM execution)
71    pub fn generate_dice_instructions(
72        &mut self,
73        expression: &str,
74    ) -> Result<Vec<JvmInstruction>, Box<dyn std::error::Error>> {
75        let mut analyzer = SemanticAnalyzer::new(expression)?;
76        let ast = analyzer.analyze()?;
77
78        if let Some(stmt) = ast.statement {
79            let crate::ast::StatementKind::Expression { expr } = stmt.kind;
80            let crate::ast::ExpressionKind::Dice { count, faces } = expr.kind;
81
82            return Ok(self.generate_dice_bytecode(count, faces)?);
83        }
84
85        Err("Invalid expression".into())
86    }
87
88    /// Setup constant pool
89    fn setup_constant_pool(&mut self) -> Result<(), String> {
90        self.constant_pool.add_utf8(self.class_name.clone())?; // UTF8 - class name
91        self.constant_pool
92            .add_utf8("java/lang/Object".to_string())?; // UTF8 - "java/lang/Object"
93        self.constant_pool.add_utf8("main".to_string())?; // UTF8 - "main"
94        self.constant_pool
95            .add_utf8("([Ljava/lang/String;)V".to_string())?; // UTF8 - "([Ljava/lang/String;)V"
96        self.constant_pool.add_utf8("Code".to_string())?; // UTF8 - "Code"
97        self.constant_pool
98            .add_utf8("java/lang/System".to_string())?; // UTF8 - "java/lang/System"
99        self.constant_pool.add_utf8("out".to_string())?; // UTF8 - "out"
100        self.constant_pool.add_utf8("err".to_string())?; // UTF8 - "err"
101        self.constant_pool
102            .add_utf8("Ljava/io/PrintStream;".to_string())?; // UTF8 - "Ljava/io/PrintStream;"
103        self.constant_pool
104            .add_utf8("java/io/PrintStream".to_string())?; // UTF8 - "java/io/PrintStream"
105        self.constant_pool.add_utf8("println".to_string())?; // UTF8 - "println"
106        self.constant_pool.add_utf8("(I)V".to_string())?; // UTF8 - "(I)V"
107        self.constant_pool.add_utf8("java/lang/Math".to_string())?; // UTF8 - "java/lang/Math"
108        self.constant_pool.add_utf8("random".to_string())?; // UTF8 - "random"
109        // 15: UTF8 - "()D"
110        self.constant_pool.add_utf8("()D".to_string())?;
111        // 16: UTF8 - "Total: "
112        self.constant_pool.add_utf8("Total: ".to_string())?;
113        // 17: UTF8 - "print"
114        self.constant_pool.add_utf8("print".to_string())?;
115        // 18: UTF8 - "(Ljava/lang/String;)V"
116        self.constant_pool
117            .add_utf8("(Ljava/lang/String;)V".to_string())?;
118
119        // Classes
120        // 19: Class - this class
121        self.constant_pool.add_class(1)?;
122        // 20: Class - java/lang/Object
123        self.constant_pool.add_class(2)?;
124        // 21: Class - java/lang/System
125        self.constant_pool.add_class(6)?;
126        // 22: Class - java/io/PrintStream
127        self.constant_pool.add_class(10)?;
128        // 23: Class - java/lang/Math
129        self.constant_pool.add_class(13)?;
130
131        // String constants
132        // 24: String - "Total: "
133        self.constant_pool.add_string(16)?;
134
135        // NameAndType
136        // 25: NameAndType - main method
137        self.constant_pool.add_name_and_type(3, 4)?;
138        // 26: NameAndType - out field
139        self.constant_pool.add_name_and_type(7, 9)?;
140        // 27: NameAndType - err field
141        self.constant_pool.add_name_and_type(8, 9)?;
142        // 28: NameAndType - println method
143        self.constant_pool.add_name_and_type(11, 12)?;
144        // 29: NameAndType - print method
145        self.constant_pool.add_name_and_type(17, 18)?;
146        // 30: NameAndType - random method
147        self.constant_pool.add_name_and_type(14, 15)?;
148
149        // Field and method references
150        // 31: Fieldref - System.out
151        self.constant_pool.add_fieldref(21, 26)?;
152        // 32: Fieldref - System.err
153        self.constant_pool.add_fieldref(21, 27)?;
154        // 33: Methodref - println
155        self.constant_pool.add_methodref(22, 28)?;
156        // 34: Methodref - print
157        self.constant_pool.add_methodref(22, 29)?;
158        // 35: Methodref - Math.random
159        self.constant_pool.add_methodref(23, 30)?;
160
161        Ok(())
162    }
163
164    /// Generate bytecode for Dice
165    fn generate_dice_bytecode(
166        &mut self,
167        count: u32,
168        faces: u32,
169    ) -> Result<Vec<JvmInstruction>, JavaClassGeneratorError> {
170        let mut instructions = Vec::new();
171
172        if count == 1 {
173            // Single dice - don't display Total
174            self.generate_single_dice(&mut instructions, faces)
175                .map_err(JavaClassGeneratorError::from)?;
176        } else {
177            // Multiple dice - display each result and Total
178            self.generate_multiple_dice(&mut instructions, count, faces)
179                .map_err(JavaClassGeneratorError::from)?;
180        }
181
182        instructions.push(JvmInstruction::Return);
183        Ok(instructions)
184    }
185
186    /// Generate bytecode for single dice
187    fn generate_single_dice(
188        &mut self,
189        instructions: &mut Vec<JvmInstruction>,
190        faces: u32,
191    ) -> Result<(), String> {
192        // Math.random() * faces + 1
193        instructions.push(JvmInstruction::Invokestatic(35)); // Math.random()
194        self.push_double_constant(instructions, faces as f64)?;
195        instructions.push(JvmInstruction::Dmul);
196        instructions.push(JvmInstruction::Dconst1);
197        instructions.push(JvmInstruction::Dadd);
198        instructions.push(JvmInstruction::D2i);
199
200        // Output result to System.out
201        instructions.push(JvmInstruction::Getstatic(31)); // System.out
202        instructions.push(JvmInstruction::Swap);
203        instructions.push(JvmInstruction::Invokevirtual(33)); // println(I)V
204        Ok(())
205    }
206
207    /// Generate bytecode for multiple dice
208    fn generate_multiple_dice(
209        &mut self,
210        instructions: &mut Vec<JvmInstruction>,
211        count: u32,
212        faces: u32,
213    ) -> Result<(), String> {
214        instructions.push(JvmInstruction::Iconst0); // total = 0
215
216        // Roll each dice
217        for _ in 0..count {
218            // Math.random() * faces + 1
219            instructions.push(JvmInstruction::Invokestatic(35)); // Math.random()
220            self.push_double_constant(instructions, faces as f64)?;
221            instructions.push(JvmInstruction::Dmul);
222            instructions.push(JvmInstruction::Dconst1);
223            instructions.push(JvmInstruction::Dadd);
224            instructions.push(JvmInstruction::D2i);
225
226            // Duplicate result (one for display, one for total)
227            instructions.push(JvmInstruction::Dup);
228
229            // Output individual result to System.out
230            instructions.push(JvmInstruction::Getstatic(31)); // System.out
231            instructions.push(JvmInstruction::Swap);
232            instructions.push(JvmInstruction::Invokevirtual(33)); // println(I)V
233
234            // Add to total
235            instructions.push(JvmInstruction::Iadd);
236        }
237
238        // Output "Total: " to System.err
239        instructions.push(JvmInstruction::Dup); // Duplicate total
240        instructions.push(JvmInstruction::Getstatic(32)); // System.err
241        instructions.push(JvmInstruction::Ldc(24)); // "Total: "
242        instructions.push(JvmInstruction::Invokevirtual(34)); // print(String)V
243
244        // Output total to System.err
245        instructions.push(JvmInstruction::Getstatic(32)); // System.err
246        instructions.push(JvmInstruction::Swap);
247        instructions.push(JvmInstruction::Invokevirtual(33)); // println(I)V
248        instructions.push(JvmInstruction::Pop); // Remove remaining value from stack
249        Ok(())
250    }
251
252    /// Push double constant to stack
253    fn push_double_constant(
254        &mut self,
255        instructions: &mut Vec<JvmInstruction>,
256        value: f64,
257    ) -> Result<(), String> {
258        if value == 0.0 {
259            instructions.push(JvmInstruction::Dconst0);
260        } else if value == 1.0 {
261            instructions.push(JvmInstruction::Dconst1);
262        } else {
263            // For more complex constants, use integer conversion
264            let int_val = value as i32;
265            self.push_int_constant(instructions, int_val)?;
266            instructions.push(JvmInstruction::I2d);
267        }
268        Ok(())
269    }
270
271    /// Push int constant to stack
272    fn push_int_constant(
273        &mut self,
274        instructions: &mut Vec<JvmInstruction>,
275        value: i32,
276    ) -> Result<(), String> {
277        match value {
278            -1 => instructions.push(JvmInstruction::IconstM1),
279            0 => instructions.push(JvmInstruction::Iconst0),
280            1 => instructions.push(JvmInstruction::Iconst1),
281            2 => instructions.push(JvmInstruction::Iconst2),
282            3 => instructions.push(JvmInstruction::Iconst3),
283            4 => instructions.push(JvmInstruction::Iconst4),
284            5 => instructions.push(JvmInstruction::Iconst5),
285            _ if (-128..=127).contains(&value) => {
286                instructions.push(JvmInstruction::Bipush(value as i8));
287            }
288            _ if (-32768..=32767).contains(&value) => {
289                instructions.push(JvmInstruction::Sipush(value as i16));
290            }
291            _ => {
292                // For values outside Sipush range, use constant pool with Ldc instruction
293                let index = self.constant_pool.add_integer(value)?;
294                instructions.push(JvmInstruction::Ldc(index));
295            }
296        }
297        Ok(())
298    }
299
300    /// Generate Java class file
301    fn generate_class_file(
302        &self,
303        bytecode_instructions: Vec<JvmInstruction>,
304    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
305        let mut bytes = Vec::new();
306
307        // Magic number
308        bytes.extend_from_slice(&0xCAFEBABEu32.to_be_bytes());
309
310        // Version (Java 8 = 52)
311        bytes.extend_from_slice(&0u16.to_be_bytes()); // Minor version
312        bytes.extend_from_slice(&52u16.to_be_bytes()); // Major version
313
314        // Constant pool count (non-placeholder entries + 1)
315        let non_placeholder_count = self
316            .constant_pool
317            .entries()
318            .iter()
319            .filter(|entry| !matches!(entry, ConstantPoolEntry::Placeholder))
320            .count();
321        bytes.extend_from_slice(&(non_placeholder_count as u16 + 1).to_be_bytes());
322
323        // Constant pool entries
324        self.write_constant_pool(&mut bytes);
325
326        // Access flags (public class)
327        bytes.extend_from_slice(&0x0021u16.to_be_bytes());
328
329        // This class (index 19)
330        bytes.extend_from_slice(&19u16.to_be_bytes());
331
332        // Super class (index 20)
333        bytes.extend_from_slice(&20u16.to_be_bytes());
334
335        // Interfaces count
336        bytes.extend_from_slice(&0u16.to_be_bytes());
337
338        // Fields count
339        bytes.extend_from_slice(&0u16.to_be_bytes());
340
341        // Methods count
342        bytes.extend_from_slice(&1u16.to_be_bytes());
343
344        // Main method
345        self.write_main_method(&mut bytes, bytecode_instructions);
346
347        // Class attributes count
348        bytes.extend_from_slice(&0u16.to_be_bytes());
349
350        Ok(bytes)
351    }
352
353    /// Write constant pool in binary format
354    fn write_constant_pool(&self, bytes: &mut Vec<u8>) {
355        for entry in self.constant_pool.entries() {
356            match entry {
357                ConstantPoolEntry::Utf8(s) => {
358                    bytes.push(1); // CONSTANT_Utf8
359                    bytes.extend_from_slice(&(s.len() as u16).to_be_bytes());
360                    bytes.extend_from_slice(s.as_bytes());
361                }
362                ConstantPoolEntry::Class(name_index) => {
363                    bytes.push(7); // CONSTANT_Class
364                    bytes.extend_from_slice(&name_index.to_be_bytes());
365                }
366                ConstantPoolEntry::String(utf8_index) => {
367                    bytes.push(8); // CONSTANT_String
368                    bytes.extend_from_slice(&utf8_index.to_be_bytes());
369                }
370                ConstantPoolEntry::Fieldref(class_index, name_and_type_index) => {
371                    bytes.push(9); // CONSTANT_Fieldref
372                    bytes.extend_from_slice(&class_index.to_be_bytes());
373                    bytes.extend_from_slice(&name_and_type_index.to_be_bytes());
374                }
375                ConstantPoolEntry::Methodref(class_index, name_and_type_index) => {
376                    bytes.push(10); // CONSTANT_Methodref
377                    bytes.extend_from_slice(&class_index.to_be_bytes());
378                    bytes.extend_from_slice(&name_and_type_index.to_be_bytes());
379                }
380                ConstantPoolEntry::NameAndType(name_index, descriptor_index) => {
381                    bytes.push(12); // CONSTANT_NameAndType
382                    bytes.extend_from_slice(&name_index.to_be_bytes());
383                    bytes.extend_from_slice(&descriptor_index.to_be_bytes());
384                }
385                ConstantPoolEntry::Integer(i) => {
386                    bytes.push(3); // CONSTANT_Integer
387                    bytes.extend_from_slice(&i.to_be_bytes());
388                }
389                ConstantPoolEntry::Float(f) => {
390                    bytes.push(4); // CONSTANT_Float
391                    bytes.extend_from_slice(&f.to_be_bytes());
392                }
393                ConstantPoolEntry::Long(l) => {
394                    bytes.push(5); // CONSTANT_Long
395                    bytes.extend_from_slice(&l.to_be_bytes());
396                }
397                ConstantPoolEntry::Double(d) => {
398                    bytes.push(6); // CONSTANT_Double
399                    bytes.extend_from_slice(&d.to_be_bytes());
400                }
401                ConstantPoolEntry::Placeholder => {
402                    // Skip placeholder entries - they should not be written to the class file
403                    // as they represent the second slot of 8-byte constants (Long/Double)
404                    // which are automatically handled by the JVM
405                    continue;
406                }
407            }
408        }
409    }
410
411    /// Write main method
412    fn write_main_method(&self, bytes: &mut Vec<u8>, instructions: Vec<JvmInstruction>) {
413        // Access flags (public static)
414        bytes.extend_from_slice(&0x0009u16.to_be_bytes());
415
416        // Name index (3 = "main")
417        bytes.extend_from_slice(&3u16.to_be_bytes());
418
419        // Descriptor index (4 = "([Ljava/lang/String;)V")
420        bytes.extend_from_slice(&4u16.to_be_bytes());
421
422        // Attributes count
423        bytes.extend_from_slice(&1u16.to_be_bytes());
424
425        // Code attribute index (5 = "Code")
426        bytes.extend_from_slice(&5u16.to_be_bytes());
427
428        // Code attribute
429        let code_bytes = self.instructions_to_bytes(instructions);
430        let attribute_length = code_bytes.len() as u32 + 12;
431
432        bytes.extend_from_slice(&attribute_length.to_be_bytes());
433        bytes.extend_from_slice(&5u16.to_be_bytes()); // max_stack
434        bytes.extend_from_slice(&2u16.to_be_bytes()); // max_locals
435        bytes.extend_from_slice(&(code_bytes.len() as u32).to_be_bytes()); // code_length
436        bytes.extend_from_slice(&code_bytes); // actual bytecode
437        bytes.extend_from_slice(&0u16.to_be_bytes()); // exception_table_length
438        bytes.extend_from_slice(&0u16.to_be_bytes()); // attributes_count
439    }
440
441    /// Convert JVM instructions to byte array
442    fn instructions_to_bytes(&self, instructions: Vec<JvmInstruction>) -> Vec<u8> {
443        let mut bytes = Vec::new();
444
445        for instruction in instructions {
446            match instruction {
447                JvmInstruction::Iconst0 => bytes.push(0x03),
448                JvmInstruction::Iconst1 => bytes.push(0x04),
449                JvmInstruction::Iconst2 => bytes.push(0x05),
450                JvmInstruction::Iconst3 => bytes.push(0x06),
451                JvmInstruction::Iconst4 => bytes.push(0x07),
452                JvmInstruction::Iconst5 => bytes.push(0x08),
453                JvmInstruction::IconstM1 => bytes.push(0x02),
454                JvmInstruction::Bipush(value) => {
455                    bytes.push(0x10);
456                    bytes.push(value as u8);
457                }
458                JvmInstruction::Sipush(value) => {
459                    bytes.push(0x11);
460                    bytes.extend_from_slice(&(value as u16).to_be_bytes());
461                }
462                JvmInstruction::Ldc(index) => {
463                    bytes.push(0x12);
464                    bytes.push(index as u8);
465                }
466                JvmInstruction::Dup => bytes.push(0x59),
467                JvmInstruction::Pop => bytes.push(0x57),
468                JvmInstruction::Swap => bytes.push(0x5F),
469                JvmInstruction::Iadd => bytes.push(0x60),
470                JvmInstruction::Isub => bytes.push(0x64),
471                JvmInstruction::Imul => bytes.push(0x68),
472                JvmInstruction::Idiv => bytes.push(0x6C),
473                JvmInstruction::Irem => bytes.push(0x70),
474                JvmInstruction::Dadd => bytes.push(0x63),
475                JvmInstruction::Dsub => bytes.push(0x67),
476                JvmInstruction::Dmul => bytes.push(0x6B),
477                JvmInstruction::Ddiv => bytes.push(0x6F),
478                JvmInstruction::I2d => bytes.push(0x87),
479                JvmInstruction::D2i => bytes.push(0x8E),
480                JvmInstruction::Dconst0 => bytes.push(0x0E),
481                JvmInstruction::Dconst1 => bytes.push(0x0F),
482                JvmInstruction::Getstatic(index) => {
483                    bytes.push(0xB2);
484                    bytes.extend_from_slice(&index.to_be_bytes());
485                }
486                JvmInstruction::Invokestatic(index) => {
487                    bytes.push(0xB8);
488                    bytes.extend_from_slice(&index.to_be_bytes());
489                }
490                JvmInstruction::Invokevirtual(index) => {
491                    bytes.push(0xB6);
492                    bytes.extend_from_slice(&index.to_be_bytes());
493                }
494                JvmInstruction::Return => bytes.push(0xB1),
495                JvmInstruction::Ireturn => bytes.push(0xAC),
496                JvmInstruction::Nop => bytes.push(0x00),
497                other_instruction => {
498                    // Log unimplemented instruction for debugging purposes
499                    // This helps identify which JVM instructions need to be implemented
500                    // when extending the compiler to support more complex expressions
501                    eprintln!(
502                        "Warning: JVM instruction not implemented in bytecode generation: {other_instruction:?}, falling back to NOP"
503                    );
504                    // Use nop as fallback
505                    bytes.push(0x00); // nop
506                }
507            }
508        }
509
510        bytes
511    }
512
513    pub fn constant_pool(&self) -> &ConstantPool {
514        &self.constant_pool
515    }
516}
517
518/// Unified JVM system - Java class file generation
519pub fn generate_java_class(
520    expression: &str,
521    class_name: &str,
522) -> Result<(), Box<dyn std::error::Error>> {
523    let mut generator = JavaClassGenerator::new(class_name.to_string());
524    let class_bytes = generator.generate_dice_class(expression)?;
525    let filename = format!("{class_name}.class");
526    fs::write(&filename, &class_bytes)?;
527
528    println!("Generated: {filename}");
529    println!("Run with: java {class_name}");
530    println!("View bytecode with: javap -c {class_name}.class");
531
532    Ok(())
533}
534
535/// Generate JVM instructions for VM execution
536pub fn generate_vm_instructions(
537    expression: &str,
538) -> Result<(Vec<JvmInstruction>, ConstantPool), Box<dyn std::error::Error>> {
539    let mut generator = JavaClassGenerator::new("DiceRoll".to_string());
540    generator
541        .setup_constant_pool()
542        .map_err(JavaClassGeneratorError::CompilationError)?;
543    let instructions = generator.generate_dice_instructions(expression)?;
544    Ok((instructions, generator.constant_pool.clone()))
545}