Skip to main content

gaia_assembler/backends/jvm/
mod.rs

1//! JVM (Java Virtual Machine) backend compiler
2
3use crate::{
4    adapters::FunctionMapper,
5    backends::{Backend, GeneratedFiles},
6    config::GaiaConfig,
7    instruction::{CoreInstruction, GaiaInstruction},
8    program::{GaiaConstant, GaiaFunction, GaiaGlobal, GaiaModule},
9    types::GaiaType,
10};
11use gaia_types::{
12    helpers::{AbiCompatible, ApiCompatible, Architecture, ArtifactType, CompilationTarget},
13    GaiaError, Result,
14};
15use std::collections::HashMap;
16
17#[cfg(feature = "jvm-assembler")]
18use jvm_assembler::{
19    formats::class::writer::ClassWriter,
20    program::{JvmAccessFlags, JvmField, JvmInstruction, JvmMethod, JvmProgram, JvmVersion},
21};
22
23#[cfg(not(feature = "jvm-assembler"))]
24mod jvm_stub {
25    pub struct JvmProgram;
26}
27#[cfg(not(feature = "jvm-assembler"))]
28use jvm_stub::*;
29
30/// JVM Backend implementation
31#[derive(Default)]
32pub struct JvmBackend {}
33
34impl Backend for JvmBackend {
35    fn name(&self) -> &'static str {
36        "JVM"
37    }
38
39    fn primary_target(&self) -> CompilationTarget {
40        CompilationTarget { build: Architecture::JVM, host: AbiCompatible::Unknown, target: ApiCompatible::JvmRuntime(8) }
41    }
42
43    fn artifact_type(&self) -> ArtifactType {
44        ArtifactType::Bytecode
45    }
46
47    fn match_score(&self, target: &CompilationTarget) -> f32 {
48        match target.build {
49            Architecture::JVM => match target.host {
50                // bytecode output, 80% support (primary)
51                AbiCompatible::Unknown => 80.0,
52                // jasm output, 5% support (disabled)
53                AbiCompatible::JavaAssembly => 5.0,
54                _ => -100.0,
55            },
56            _ => -100.0,
57        }
58    }
59
60    fn generate(&self, program: &GaiaModule, config: &GaiaConfig) -> Result<GeneratedFiles> {
61        #[cfg(feature = "jvm-assembler")]
62        {
63            let mut files = HashMap::new();
64
65            // Convert GaiaModule to JvmProgram (with config and function mapping)
66            let jvm_program = convert_gaia_to_jvm(program, config)?;
67
68            match config.target.host {
69                AbiCompatible::Unknown => {
70                    // Generate .class bytecode file
71                    let buffer = Vec::new();
72                    let class_writer = ClassWriter::new(buffer);
73                    let class_bytes = class_writer.write(&jvm_program).result?;
74                    files.insert("main.class".to_string(), class_bytes);
75                }
76                AbiCompatible::JavaAssembly => {
77                    return Err(GaiaError::custom_error("JASM output is currently disabled"));
78                }
79                _ => return Err(GaiaError::custom_error(&format!("Unsupported host ABI: {:?}", config.target.host))),
80            }
81
82            Ok(GeneratedFiles { artifact_type: self.artifact_type(), files, custom: None, diagnostics: vec![] })
83        }
84        #[cfg(not(feature = "jvm-assembler"))]
85        {
86            let _ = program;
87            let _ = config;
88            Err(gaia_types::errors::GaiaError::custom_error("JVM backend not enabled"))
89        }
90    }
91}
92
93impl JvmBackend {
94    /// Generate JVM program from Gaia program
95    pub fn generate_program(program: &GaiaModule) -> Result<JvmProgram> {
96        #[cfg(feature = "jvm-assembler")]
97        {
98            // Generate with default configuration (maintain backward compatibility)
99            let default_config = GaiaConfig::default();
100            convert_gaia_to_jvm(program, &default_config)
101        }
102        #[cfg(not(feature = "jvm-assembler"))]
103        {
104            let _ = program;
105            Err(gaia_types::errors::GaiaError::custom_error("JVM backend not enabled"))
106        }
107    }
108}
109
110#[cfg(feature = "jvm-assembler")]
111/// JVM compilation context, carrying function mapping and target information
112struct JvmContext {
113    function_mapper: FunctionMapper,
114    /// Field type mapping (class name, field name) -> descriptor
115    field_types: HashMap<(String, String), String>,
116}
117
118#[cfg(feature = "jvm-assembler")]
119/// Convert GaiaModule to JvmProgram
120fn convert_gaia_to_jvm(program: &GaiaModule, config: &GaiaConfig) -> Result<JvmProgram> {
121    let mut jvm_program = JvmProgram::new(program.name.clone());
122
123    // Set version information
124    jvm_program.version = JvmVersion { major: 52, minor: 0 }; // Java 8
125
126    // Set access flags
127    jvm_program.access_flags = JvmAccessFlags::public();
128
129    // Build field type mapping
130    let mut field_types = HashMap::new();
131    for class in &program.classes {
132        for field in &class.fields {
133            field_types.insert((class.name.clone(), field.name.clone()), convert_gaia_type_to_jvm_descriptor(&field.ty));
134        }
135    }
136    for global in &program.globals {
137        field_types.insert(("Main".to_string(), global.name.clone()), convert_gaia_type_to_jvm_descriptor(&global.ty));
138    }
139
140    // Build context (initialize function mapping from config)
141    let ctx = JvmContext { function_mapper: FunctionMapper::from_config(&config.setting)?, field_types };
142
143    // Convert functions (with context)
144    for function in &program.functions {
145        let jvm_method = convert_gaia_function_to_jvm(function, &ctx)?;
146        jvm_program.add_method(jvm_method);
147    }
148
149    // Convert classes
150    for class in &program.classes {
151        for field in &class.fields {
152            let jvm_field = convert_gaia_field_to_jvm_field(field)?;
153            jvm_program.add_field(jvm_field);
154        }
155        for method in &class.methods {
156            let jvm_method = convert_gaia_function_to_jvm(method, &ctx)?;
157            jvm_program.add_method(jvm_method);
158        }
159    }
160
161    // Convert global variables to fields
162    for global in &program.globals {
163        let jvm_field = convert_gaia_global_to_jvm_field(global)?;
164        jvm_program.add_field(jvm_field);
165    }
166
167    Ok(jvm_program)
168}
169
170#[cfg(feature = "jvm-assembler")]
171/// Convert GaiaFunction to JvmMethod
172fn convert_gaia_function_to_jvm(function: &GaiaFunction, ctx: &JvmContext) -> Result<JvmMethod> {
173    // Build method descriptor
174    let descriptor = build_method_descriptor(&function.signature.params, &Some(function.signature.return_type.clone()));
175
176    let mut method = JvmMethod::new(function.name.clone(), descriptor);
177
178    // Set access flags
179    method.access_flags.is_public = true;
180    method.access_flags.is_static = true;
181
182    // Convert basic blocks
183    for block in &function.blocks {
184        for instruction in &block.instructions {
185            let jvm_instructions = convert_gaia_instruction_to_jvm(instruction, ctx)?;
186            for ji in jvm_instructions {
187                method.add_instruction(ji);
188            }
189        }
190
191        // Handle terminator
192        match &block.terminator {
193            crate::program::GaiaTerminator::Jump(label) => {
194                method.add_instruction(JvmInstruction::Goto { target: label.clone() });
195            }
196            crate::program::GaiaTerminator::Branch { true_label, false_label } => {
197                // JVM usually jumps on false condition first, or true jump. Here we use Ifne (if not zero)
198                method.add_instruction(JvmInstruction::Ifne { target: true_label.clone() });
199                method.add_instruction(JvmInstruction::Goto { target: false_label.clone() });
200            }
201            crate::program::GaiaTerminator::Return => {
202                method.add_instruction(JvmInstruction::Return);
203            }
204            crate::program::GaiaTerminator::Call { callee, args_count: _, next_block } => {
205                // JVM call mapping
206                let jvm_target = CompilationTarget {
207                    build: Architecture::JVM,
208                    host: AbiCompatible::JavaAssembly,
209                    target: ApiCompatible::JvmRuntime(8),
210                };
211                let mapped = ctx.function_mapper.map_function(&jvm_target, callee).unwrap_or(callee.as_str()).to_string();
212
213                method.add_instruction(JvmInstruction::Invokestatic {
214                    class_name: "Main".to_string(),
215                    method_name: mapped,
216                    descriptor: "()V".to_string(), // Simplified, actually needs to be determined by function signature
217                });
218                method.add_instruction(JvmInstruction::Goto { target: next_block.clone() });
219            }
220            crate::program::GaiaTerminator::Halt => {
221                // In JVM, Halt can be mapped to System.exit(0)
222                method.add_instruction(JvmInstruction::Iconst0);
223                method.add_instruction(JvmInstruction::Invokestatic {
224                    class_name: "java/lang/System".to_string(),
225                    method_name: "exit".to_string(),
226                    descriptor: "(I)V".to_string(),
227                });
228            }
229        }
230    }
231
232    // Set stack and local variable sizes (simplified)
233    method.max_stack = 10;
234    method.max_locals = 10;
235
236    Ok(method)
237}
238
239#[cfg(feature = "jvm-assembler")]
240/// Convert GaiaField to JvmField
241fn convert_gaia_field_to_jvm_field(field: &crate::program::GaiaField) -> Result<JvmField> {
242    let descriptor = convert_gaia_type_to_jvm_descriptor(&field.ty);
243    let mut jvm_field = JvmField::new(field.name.clone(), descriptor);
244
245    if field.is_static {
246        jvm_field.access_flags.is_static = true;
247    }
248
249    match field.visibility {
250        crate::program::Visibility::Public => jvm_field.access_flags.is_public = true,
251        crate::program::Visibility::Private => jvm_field.access_flags.is_private = true,
252        crate::program::Visibility::Protected => jvm_field.access_flags.is_protected = true,
253        _ => {}
254    }
255
256    Ok(jvm_field)
257}
258
259#[cfg(feature = "jvm-assembler")]
260/// Convert GaiaGlobal to JvmField
261fn convert_gaia_global_to_jvm_field(global: &GaiaGlobal) -> Result<JvmField> {
262    let descriptor = convert_gaia_type_to_jvm_descriptor(&global.ty);
263    let mut field = JvmField::new(global.name.clone(), descriptor);
264    field.access_flags.is_public = true;
265    field.access_flags.is_static = true;
266
267    Ok(field)
268}
269
270#[cfg(feature = "jvm-assembler")]
271/// Convert GaiaInstruction to JvmInstruction
272fn convert_gaia_instruction_to_jvm(instruction: &GaiaInstruction, ctx: &JvmContext) -> Result<Vec<JvmInstruction>> {
273    match instruction {
274        GaiaInstruction::Core(core) => match core {
275            CoreInstruction::PushConstant(constant) => match constant {
276                GaiaConstant::I8(value) => Ok(vec![JvmInstruction::Bipush { value: *value }]),
277                GaiaConstant::U8(value) => Ok(vec![JvmInstruction::Bipush { value: *value as i8 }]),
278                GaiaConstant::I16(value) => Ok(vec![JvmInstruction::Sipush { value: *value }]),
279                GaiaConstant::U16(value) => Ok(vec![JvmInstruction::Sipush { value: *value as i16 }]),
280                GaiaConstant::I32(value) => match *value {
281                    0 => Ok(vec![JvmInstruction::Iconst0]),
282                    1 => Ok(vec![JvmInstruction::Iconst1]),
283                    2 => Ok(vec![JvmInstruction::Iconst2]),
284                    3 => Ok(vec![JvmInstruction::Iconst3]),
285                    4 => Ok(vec![JvmInstruction::Iconst4]),
286                    5 => Ok(vec![JvmInstruction::Iconst5]),
287                    -1 => Ok(vec![JvmInstruction::IconstM1]),
288                    _ if *value >= -128 && *value <= 127 => Ok(vec![JvmInstruction::Bipush { value: *value as i8 }]),
289                    _ if *value >= -32768 && *value <= 32767 => Ok(vec![JvmInstruction::Sipush { value: *value as i16 }]),
290                    _ => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
291                },
292                GaiaConstant::U32(value) => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
293                GaiaConstant::I64(value) => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
294                GaiaConstant::U64(value) => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
295                GaiaConstant::F32(value) => match *value {
296                    0.0 => Ok(vec![JvmInstruction::Fconst0]),
297                    1.0 => Ok(vec![JvmInstruction::Fconst1]),
298                    2.0 => Ok(vec![JvmInstruction::Fconst2]),
299                    _ => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
300                },
301                GaiaConstant::F64(value) => match *value {
302                    0.0 => Ok(vec![JvmInstruction::Dconst0]),
303                    1.0 => Ok(vec![JvmInstruction::Dconst1]),
304                    _ => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
305                },
306                GaiaConstant::String(value) => Ok(vec![JvmInstruction::Ldc { symbol: value.clone() }]),
307                GaiaConstant::Bool(value) => Ok(vec![if *value { JvmInstruction::Iconst1 } else { JvmInstruction::Iconst0 }]),
308                GaiaConstant::Null => Ok(vec![JvmInstruction::AconstNull]),
309                _ => Err(GaiaError::custom_error("Unsupported constant type for JVM")),
310            },
311            CoreInstruction::Load(_gaia_type) => Err(GaiaError::not_implemented("JVM indirect load")),
312            CoreInstruction::Store(_gaia_type) => Err(GaiaError::not_implemented("JVM indirect store")),
313            CoreInstruction::Add(_) => Ok(vec![JvmInstruction::Iadd]),
314            CoreInstruction::Sub(_) => Ok(vec![JvmInstruction::Isub]),
315            CoreInstruction::Mul(_) => Ok(vec![JvmInstruction::Imul]),
316            CoreInstruction::Div(_) => Ok(vec![JvmInstruction::Idiv]),
317            CoreInstruction::Pop => Ok(vec![JvmInstruction::Pop]),
318            CoreInstruction::Dup => Ok(vec![JvmInstruction::Dup]),
319            CoreInstruction::Ret => Ok(vec![JvmInstruction::Return]),
320            CoreInstruction::Br(label) => Ok(vec![JvmInstruction::Goto { target: label.clone() }]),
321            CoreInstruction::BrTrue(label) => Ok(vec![JvmInstruction::Ifne { target: label.clone() }]),
322            CoreInstruction::BrFalse(label) => Ok(vec![JvmInstruction::Ifeq { target: label.clone() }]),
323            CoreInstruction::Label(name) => Ok(vec![JvmInstruction::Label { name: name.clone() }]),
324            CoreInstruction::Call(name, arg_count) => {
325                let jvm_target = gaia_types::helpers::CompilationTarget {
326                    build: gaia_types::helpers::Architecture::JVM,
327                    host: gaia_types::helpers::AbiCompatible::JavaAssembly,
328                    target: gaia_types::helpers::ApiCompatible::JvmRuntime(8),
329                };
330                let mapped = ctx.function_mapper.map_function(&jvm_target, name).unwrap_or(name);
331                Ok(vec![JvmInstruction::Invokestatic {
332                    class_name: "Main".to_string(),
333                    method_name: mapped.to_string(),
334                    descriptor: format!("({})V", "I".repeat(*arg_count)),
335                }])
336            }
337            CoreInstruction::LoadLocal(index, ty) => Ok(vec![match ty {
338                GaiaType::I32
339                | GaiaType::U32
340                | GaiaType::Bool
341                | GaiaType::I8
342                | GaiaType::U8
343                | GaiaType::I16
344                | GaiaType::U16 => JvmInstruction::Iload { index: *index as u16 },
345                GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lload { index: *index as u16 },
346                GaiaType::F32 => JvmInstruction::Fload { index: *index as u16 },
347                GaiaType::F64 => JvmInstruction::Dload { index: *index as u16 },
348                _ => JvmInstruction::Aload { index: *index as u16 },
349            }]),
350            CoreInstruction::StoreLocal(index, ty) => Ok(vec![match ty {
351                GaiaType::I32
352                | GaiaType::U32
353                | GaiaType::Bool
354                | GaiaType::I8
355                | GaiaType::U8
356                | GaiaType::I16
357                | GaiaType::U16 => JvmInstruction::Istore { index: *index as u16 },
358                GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lstore { index: *index as u16 },
359                GaiaType::F32 => JvmInstruction::Fstore { index: *index as u16 },
360                GaiaType::F64 => JvmInstruction::Dstore { index: *index as u16 },
361                _ => JvmInstruction::Astore { index: *index as u16 },
362            }]),
363            CoreInstruction::LoadArg(index, ty) => Ok(vec![match ty {
364                GaiaType::I32
365                | GaiaType::U32
366                | GaiaType::Bool
367                | GaiaType::I8
368                | GaiaType::U8
369                | GaiaType::I16
370                | GaiaType::U16 => JvmInstruction::Iload { index: *index as u16 },
371                GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lload { index: *index as u16 },
372                GaiaType::F32 => JvmInstruction::Fload { index: *index as u16 },
373                GaiaType::F64 => JvmInstruction::Dload { index: *index as u16 },
374                _ => JvmInstruction::Aload { index: *index as u16 },
375            }]),
376            CoreInstruction::StoreArg(index, ty) => Ok(vec![match ty {
377                GaiaType::I32
378                | GaiaType::U32
379                | GaiaType::Bool
380                | GaiaType::I8
381                | GaiaType::U8
382                | GaiaType::I16
383                | GaiaType::U16 => JvmInstruction::Istore { index: *index as u16 },
384                GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lstore { index: *index as u16 },
385                GaiaType::F32 => JvmInstruction::Fstore { index: *index as u16 },
386                GaiaType::F64 => JvmInstruction::Dstore { index: *index as u16 },
387                _ => JvmInstruction::Astore { index: *index as u16 },
388            }]),
389            CoreInstruction::New(type_name) => Ok(vec![
390                JvmInstruction::New { class_name: type_name.replace('.', "/") },
391                JvmInstruction::Dup,
392                JvmInstruction::Invokespecial {
393                    class_name: type_name.replace('.', "/"),
394                    method_name: "<init>".to_string(),
395                    descriptor: "()V".to_string(),
396                },
397            ]),
398            CoreInstruction::LoadField(type_name, field_name) => {
399                let descriptor = ctx
400                    .field_types
401                    .get(&(type_name.clone(), field_name.clone()))
402                    .cloned()
403                    .unwrap_or_else(|| "Ljava/lang/Object;".to_string());
404                Ok(vec![JvmInstruction::Getfield {
405                    class_name: type_name.replace('.', "/"),
406                    field_name: field_name.to_string(),
407                    descriptor,
408                }])
409            }
410            CoreInstruction::StoreField(type_name, field_name) => {
411                let descriptor = ctx
412                    .field_types
413                    .get(&(type_name.clone(), field_name.clone()))
414                    .cloned()
415                    .unwrap_or_else(|| "Ljava/lang/Object;".to_string());
416                Ok(vec![JvmInstruction::Putfield {
417                    class_name: type_name.replace('.', "/"),
418                    field_name: field_name.to_string(),
419                    descriptor,
420                }])
421            }
422            CoreInstruction::LoadElement(ty) => Ok(vec![match ty {
423                GaiaType::I32 | GaiaType::U32 => JvmInstruction::Iaload,
424                GaiaType::I64 | GaiaType::U64 => JvmInstruction::Laload,
425                GaiaType::F32 => JvmInstruction::Faload,
426                GaiaType::F64 => JvmInstruction::Daload,
427                GaiaType::I8 | GaiaType::U8 | GaiaType::Bool => JvmInstruction::Baload,
428                GaiaType::I16 | GaiaType::U16 => JvmInstruction::Saload,
429                _ => JvmInstruction::Aaload,
430            }]),
431            CoreInstruction::StoreElement(ty) => Ok(vec![match ty {
432                GaiaType::I32 | GaiaType::U32 => JvmInstruction::Iastore,
433                GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lastore,
434                GaiaType::F32 => JvmInstruction::Fastore,
435                GaiaType::F64 => JvmInstruction::Dastore,
436                GaiaType::I8 | GaiaType::U8 | GaiaType::Bool => JvmInstruction::Bastore,
437                GaiaType::I16 | GaiaType::U16 => JvmInstruction::Sastore,
438                _ => JvmInstruction::Aastore,
439            }]),
440            _ => Ok(vec![]),
441        },
442        _ => Ok(vec![]),
443    }
444}
445
446#[cfg(feature = "jvm-assembler")]
447/// Convert GaiaType to JVM descriptor
448fn convert_gaia_type_to_jvm_descriptor(ty: &GaiaType) -> String {
449    match ty {
450        GaiaType::Void => "V".to_string(),
451        GaiaType::Bool => "Z".to_string(),
452        GaiaType::I8 => "B".to_string(),
453        GaiaType::U8 => "B".to_string(),
454        GaiaType::I16 => "S".to_string(),
455        GaiaType::U16 => "S".to_string(),
456        GaiaType::I32 => "I".to_string(),
457        GaiaType::U32 => "I".to_string(),
458        GaiaType::I64 => "J".to_string(),
459        GaiaType::U64 => "J".to_string(),
460        GaiaType::F32 => "F".to_string(),
461        GaiaType::F64 => "D".to_string(),
462        GaiaType::String => "Ljava/lang/String;".to_string(),
463        GaiaType::Class(name) => format!("L{};", name.replace('.', "/")),
464        GaiaType::Array(inner, _) => format!("[{}", convert_gaia_type_to_jvm_descriptor(inner)),
465        GaiaType::Object => "Ljava/lang/Object;".to_string(),
466        _ => "Ljava/lang/Object;".to_string(),
467    }
468}
469
470#[cfg(feature = "jvm-assembler")]
471/// Build JVM method descriptor from Gaia types
472fn build_method_descriptor(params: &[GaiaType], return_type: &Option<GaiaType>) -> String {
473    let mut descriptor = "(".to_string();
474    for param in params {
475        descriptor.push_str(&convert_gaia_type_to_jvm_descriptor(param));
476    }
477    descriptor.push(')');
478    if let Some(ret) = return_type {
479        descriptor.push_str(&convert_gaia_type_to_jvm_descriptor(ret));
480    }
481    else {
482        descriptor.push('V');
483    }
484    descriptor
485}