gaia_assembler/backends/jvm/
mod.rs1use crate::{
4 backends::{Backend, GeneratedFiles},
5 config::GaiaConfig,
6 instruction::GaiaInstruction,
7 program::{GaiaFunction, GaiaProgram},
8 types::GaiaType,
9};
10use gaia_types::{
11 helpers::{AbiCompatible, ApiCompatible, Architecture, CompilationTarget},
12 GaiaError, Result,
13};
14use jvm_assembler::{
15 formats::{class::writer::ClassWriter, jasm::ast::to_jasm::JvmToJasmConverter},
16 program::{JvmAccessFlags, JvmField, JvmInstruction, JvmMethod, JvmProgram, JvmVersion},
17};
18use std::collections::HashMap;
19use crate::program::{GaiaConstant, GaiaGlobal};
20
21#[derive(Default)]
23pub struct JvmBackend {}
24
25impl Backend for JvmBackend {
26 fn name(&self) -> &'static str {
27 "JVM"
28 }
29
30 fn primary_target(&self) -> CompilationTarget {
31 CompilationTarget { build: Architecture::JVM, host: AbiCompatible::JavaAssembly, target: ApiCompatible::JvmRuntime(8) }
32 }
33
34 fn match_score(&self, target: &CompilationTarget) -> f32 {
35 match target.build {
36 Architecture::JVM => match target.host {
37 AbiCompatible::JavaAssembly => 5.0,
39 AbiCompatible::Unknown => 30.0,
41 _ => -100.0,
42 },
43 _ => -100.0,
44 }
45 }
46
47 fn generate(&self, program: &GaiaProgram, config: &GaiaConfig) -> Result<GeneratedFiles> {
48 let mut files = HashMap::new();
49
50 let jvm_program = convert_gaia_to_jvm(program)?;
52
53 match config.target.host {
54 AbiCompatible::Unknown => {
55 let buffer = Vec::new();
57 let mut class_writer = ClassWriter::new(buffer);
58 let class_bytes = class_writer.write(&jvm_program).result?;
59 files.insert("main.class".to_string(), class_bytes);
60 }
61 AbiCompatible::JavaAssembly => {
62 let mut converter = JvmToJasmConverter::new();
64 let jasm_result = converter.convert(jvm_program);
65 match jasm_result.result {
66 Ok(jasm_root) => {
67 let jasm_string = format!("{:#?}", jasm_root);
68 files.insert("main.jasm".to_string(), jasm_string.into_bytes());
69 }
70 Err(error) => return Err(error),
71 }
72 }
73 _ => return Err(GaiaError::custom_error(&format!("Unsupported host ABI: {:?}", config.target.host))),
74 }
75
76 Ok(GeneratedFiles { files, diagnostics: vec![] })
77 }
78}
79
80impl JvmBackend {
81 pub fn generate_program(program: &GaiaProgram) -> Result<JvmProgram> {
83 convert_gaia_to_jvm(program)
84 }
85}
86
87fn convert_gaia_to_jvm(program: &GaiaProgram) -> Result<JvmProgram> {
89 let mut jvm_program = JvmProgram::new(program.name.clone());
90
91 jvm_program.version = JvmVersion { major: 52, minor: 0 }; jvm_program.access_flags = JvmAccessFlags::PUBLIC;
96
97 for function in &program.functions {
99 let jvm_method = convert_gaia_function_to_jvm(function)?;
100 jvm_program.add_method(jvm_method);
101 }
102
103 if let Some(globals) = &program.globals {
105 for global in globals {
106 let jvm_field = convert_gaia_global_to_jvm_field(global)?;
107 jvm_program.add_field(jvm_field);
108 }
109 }
110
111 Ok(jvm_program)
112}
113
114fn convert_gaia_function_to_jvm(function: &GaiaFunction) -> Result<JvmMethod> {
116 let descriptor = build_method_descriptor(&function.parameters, &function.return_type);
118
119 let mut method = JvmMethod::new(function.name.clone(), descriptor);
120
121 method = method.with_public().with_static();
123
124 for instruction in &function.instructions {
126 let jvm_instruction = convert_gaia_instruction_to_jvm(instruction)?;
127 method = method.with_instruction(jvm_instruction);
128 }
129
130 method = method.with_max_stack(10).with_max_locals(10);
132
133 Ok(method)
134}
135
136fn convert_gaia_global_to_jvm_field(global: &GaiaGlobal) -> Result<JvmField> {
138 let descriptor = convert_gaia_type_to_jvm_descriptor(&global.var_type);
139 let field = JvmField::new(global.name.clone(), descriptor).with_public().with_static();
140
141 Ok(field)
142}
143
144fn convert_gaia_instruction_to_jvm(instruction: &GaiaInstruction) -> Result<JvmInstruction> {
146 match instruction {
147 GaiaInstruction::LoadConstant(constant) => match constant {
148 GaiaConstant::Integer64(value) => match *value {
149 0 => Ok(JvmInstruction::Iconst0),
150 1 => Ok(JvmInstruction::Iconst1),
151 2 => Ok(JvmInstruction::Iconst2),
152 3 => Ok(JvmInstruction::Iconst3),
153 4 => Ok(JvmInstruction::Iconst4),
154 5 => Ok(JvmInstruction::Iconst5),
155 -1 => Ok(JvmInstruction::IconstM1),
156 _ if *value >= -128 && *value <= 127 => Ok(JvmInstruction::Bipush { value: *value as i8 }),
157 _ if *value >= -32768 && *value <= 32767 => Ok(JvmInstruction::Sipush { value: *value as i16 }),
158 _ => Ok(JvmInstruction::Ldc { symbol: value.to_string() }),
159 },
160 GaiaConstant::Float64(value) => match *value {
161 0.0 => Ok(JvmInstruction::Fconst0),
162 1.0 => Ok(JvmInstruction::Fconst1),
163 2.0 => Ok(JvmInstruction::Fconst2),
164 _ => Ok(JvmInstruction::Ldc { symbol: value.to_string() }),
165 },
166 GaiaConstant::String(value) => Ok(JvmInstruction::Ldc { symbol: value.clone() }),
167 _ => Err(GaiaError::custom_error("Unsupported constant type for JVM")),
168 },
169 GaiaInstruction::LoadLocal(index) => match *index {
170 0 => Ok(JvmInstruction::Iload0),
171 1 => Ok(JvmInstruction::Iload1),
172 2 => Ok(JvmInstruction::Iload2),
173 3 => Ok(JvmInstruction::Iload3),
174 _ => Ok(JvmInstruction::Iload { index: *index as u16 }),
175 },
176 GaiaInstruction::StoreLocal(index) => match *index {
177 0 => Ok(JvmInstruction::Istore0),
178 1 => Ok(JvmInstruction::Istore1),
179 2 => Ok(JvmInstruction::Istore2),
180 3 => Ok(JvmInstruction::Istore3),
181 _ => Ok(JvmInstruction::Istore { index: *index as u16 }),
182 },
183 GaiaInstruction::Add => Ok(JvmInstruction::Iadd),
185 GaiaInstruction::Subtract => Ok(JvmInstruction::Isub),
186 GaiaInstruction::Multiply => Ok(JvmInstruction::Imul),
187 GaiaInstruction::Divide => Ok(JvmInstruction::Idiv),
188 GaiaInstruction::Remainder => Ok(JvmInstruction::Irem),
189 GaiaInstruction::BitwiseAnd => Ok(JvmInstruction::Iand),
190 GaiaInstruction::BitwiseOr => Ok(JvmInstruction::Ior),
191 GaiaInstruction::BitwiseXor => Ok(JvmInstruction::Ixor),
192 GaiaInstruction::BitwiseNot => {
193 Err(GaiaError::custom_error("BitwiseNot not directly supported in JVM"))
195 }
196 GaiaInstruction::ShiftLeft => Ok(JvmInstruction::Ishl),
197 GaiaInstruction::ShiftRight => Ok(JvmInstruction::Ishr),
198 GaiaInstruction::Equal => {
199 Err(GaiaError::custom_error("Equal comparison requires conditional branching in JVM"))
201 }
202 GaiaInstruction::NotEqual => Err(GaiaError::custom_error("NotEqual comparison requires conditional branching in JVM")),
203 GaiaInstruction::LessThan => Err(GaiaError::custom_error("LessThan comparison requires conditional branching in JVM")),
204 GaiaInstruction::GreaterThan => {
205 Err(GaiaError::custom_error("GreaterThan comparison requires conditional branching in JVM"))
206 }
207 GaiaInstruction::LessThanOrEqual => {
208 Err(GaiaError::custom_error("LessThanOrEqual comparison requires conditional branching in JVM"))
209 }
210 GaiaInstruction::GreaterThanOrEqual => {
211 Err(GaiaError::custom_error("GreaterThanOrEqual comparison requires conditional branching in JVM"))
212 }
213 GaiaInstruction::Jump(label) => Ok(JvmInstruction::Goto { target: label.clone() }),
214 GaiaInstruction::JumpIfTrue(label) => Ok(JvmInstruction::Ifne { target: label.clone() }),
215 GaiaInstruction::JumpIfFalse(label) => Ok(JvmInstruction::Ifeq { target: label.clone() }),
216 GaiaInstruction::Call(function_name, _arg_count) => {
217 Ok(JvmInstruction::Invokestatic {
219 class_name: "Main".to_string(),
220 method_name: function_name.clone(),
221 descriptor: "()V".to_string(), })
223 }
224 GaiaInstruction::Return => Ok(JvmInstruction::Return),
225 GaiaInstruction::Label(_name) => {
226 Err(GaiaError::custom_error("Labels are handled during assembly, not as instructions"))
228 }
229 GaiaInstruction::Duplicate => Ok(JvmInstruction::Dup),
230 GaiaInstruction::Pop => Ok(JvmInstruction::Pop),
231 _ => Err(GaiaError::custom_error(&format!("Unsupported instruction for JVM: {:?}", instruction))),
232 }
233}
234
235fn build_method_descriptor(parameters: &[GaiaType], return_type: &Option<GaiaType>) -> String {
237 let mut descriptor = String::from("(");
238
239 for param in parameters {
241 descriptor.push_str(&convert_gaia_type_to_jvm_descriptor(param));
242 }
243
244 descriptor.push(')');
245
246 match return_type {
248 Some(ret_type) => descriptor.push_str(&convert_gaia_type_to_jvm_descriptor(ret_type)),
249 None => descriptor.push('V'), }
251
252 descriptor
253}
254
255fn convert_gaia_type_to_jvm_descriptor(gaia_type: &GaiaType) -> String {
257 match gaia_type {
258 GaiaType::Integer => "I".to_string(),
259 GaiaType::Float => "F".to_string(),
260 GaiaType::Double => "D".to_string(),
261 GaiaType::Boolean => "Z".to_string(),
262 GaiaType::String => "Ljava/lang/String;".to_string(),
263 GaiaType::Void => "V".to_string(),
264 _ => "Ljava/lang/Object;".to_string(), }
266}