1use 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, CompilationTarget},
13 GaiaError, Result,
14};
15use jvm_assembler::{
16 formats::{class::writer::ClassWriter },
17 program::{JvmAccessFlags, JvmField, JvmInstruction, JvmMethod, JvmProgram, JvmVersion},
18};
19use std::collections::HashMap;
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::Unknown, 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::Unknown => 80.0,
39 AbiCompatible::JavaAssembly => 5.0,
41 _ => -100.0,
42 },
43 _ => -100.0,
44 }
45 }
46
47 fn generate(&self, program: &GaiaModule, config: &GaiaConfig) -> Result<GeneratedFiles> {
48 let mut files = HashMap::new();
49
50 let jvm_program = convert_gaia_to_jvm(program, config)?;
52
53 match config.target.host {
54 AbiCompatible::Unknown => {
55 let buffer = Vec::new();
57 let 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 return Err(GaiaError::custom_error("JASM output is currently disabled"));
73 }
74 _ => return Err(GaiaError::custom_error(&format!("Unsupported host ABI: {:?}", config.target.host))),
75 }
76
77 Ok(GeneratedFiles { files, diagnostics: vec![] })
78 }
79}
80
81impl JvmBackend {
82 pub fn generate_program(program: &GaiaModule) -> Result<JvmProgram> {
84 let default_config = GaiaConfig::default();
86 convert_gaia_to_jvm(program, &default_config)
87 }
88}
89
90struct JvmContext {
92 function_mapper: FunctionMapper,
93 field_types: HashMap<(String, String), String>,
95}
96
97fn convert_gaia_to_jvm(program: &GaiaModule, config: &GaiaConfig) -> Result<JvmProgram> {
99 let mut jvm_program = JvmProgram::new(program.name.clone());
100
101 jvm_program.version = JvmVersion { major: 52, minor: 0 }; jvm_program.access_flags = JvmAccessFlags::PUBLIC;
106
107 let mut field_types = HashMap::new();
109 for class in &program.classes {
110 for field in &class.fields {
111 field_types.insert((class.name.clone(), field.name.clone()), convert_gaia_type_to_jvm_descriptor(&field.ty));
112 }
113 }
114 for global in &program.globals {
115 field_types.insert(("Main".to_string(), global.name.clone()), convert_gaia_type_to_jvm_descriptor(&global.ty));
116 }
117
118 let ctx = JvmContext { function_mapper: FunctionMapper::from_config(&config.setting)?, field_types };
120
121 for function in &program.functions {
126 let jvm_method = convert_gaia_function_to_jvm(function, &ctx)?;
127 jvm_program.add_method(jvm_method);
128 }
129
130 for class in &program.classes {
132 for field in &class.fields {
137 let jvm_field = convert_gaia_field_to_jvm_field(field)?;
138 jvm_program.add_field(jvm_field);
139 }
140 for method in &class.methods {
141 let jvm_method = convert_gaia_function_to_jvm(method, &ctx)?;
142 jvm_program.add_method(jvm_method);
143 }
144 }
145
146 for global in &program.globals {
148 let jvm_field = convert_gaia_global_to_jvm_field(global)?;
149 jvm_program.add_field(jvm_field);
150 }
151
152 Ok(jvm_program)
153}
154
155fn convert_gaia_function_to_jvm(function: &GaiaFunction, ctx: &JvmContext) -> Result<JvmMethod> {
157 let descriptor = build_method_descriptor(&function.signature.params, &Some(function.signature.return_type.clone()));
159
160 let mut method = JvmMethod::new(function.name.clone(), descriptor);
161
162 method = method.with_public().with_static();
164
165 for block in &function.blocks {
167 for instruction in &block.instructions {
172 let jvm_instructions = convert_gaia_instruction_to_jvm(instruction, ctx)?;
173 for ji in jvm_instructions {
174 method = method.with_instruction(ji);
175 }
176 }
177
178 match &block.terminator {
180 crate::program::GaiaTerminator::Jump(label) => {
181 method = method.with_instruction(JvmInstruction::Goto { target: label.clone() });
182 }
183 crate::program::GaiaTerminator::Branch { true_label, false_label } => {
184 method = method.with_instruction(JvmInstruction::Ifne { target: true_label.clone() });
186 method = method.with_instruction(JvmInstruction::Goto { target: false_label.clone() });
187 }
188 crate::program::GaiaTerminator::Return => {
189 method = method.with_instruction(JvmInstruction::Return);
190 }
191 crate::program::GaiaTerminator::Call { callee, args_count: _, next_block } => {
192 let jvm_target = CompilationTarget {
194 build: Architecture::JVM,
195 host: AbiCompatible::JavaAssembly,
196 target: ApiCompatible::JvmRuntime(8),
197 };
198 let mapped = ctx.function_mapper.map_function(&jvm_target, callee).unwrap_or(callee.as_str()).to_string();
199
200 method = method.with_instruction(JvmInstruction::Invokestatic {
201 class_name: "Main".to_string(),
202 method_name: mapped,
203 descriptor: "()V".to_string(), });
205 method = method.with_instruction(JvmInstruction::Goto { target: next_block.clone() });
206 }
207 crate::program::GaiaTerminator::Halt => {
208 method = method.with_instruction(JvmInstruction::Iconst0);
210 method = method.with_instruction(JvmInstruction::Invokestatic {
211 class_name: "java/lang/System".to_string(),
212 method_name: "exit".to_string(),
213 descriptor: "(I)V".to_string(),
214 });
215 }
216 }
217 }
218
219 method = method.with_max_stack(10).with_max_locals(10);
221
222 Ok(method)
223}
224
225fn convert_gaia_field_to_jvm_field(field: &crate::program::GaiaField) -> Result<JvmField> {
227 let descriptor = convert_gaia_type_to_jvm_descriptor(&field.ty);
228 let mut jvm_field = JvmField::new(field.name.clone(), descriptor);
229
230 if field.is_static {
231 jvm_field = jvm_field.with_static();
232 }
233
234 match field.visibility {
235 crate::program::Visibility::Public => jvm_field = jvm_field.with_public(),
236 crate::program::Visibility::Private => jvm_field = jvm_field.with_private(),
237 crate::program::Visibility::Protected => jvm_field = jvm_field.with_protected(),
238 _ => {}
239 }
240
241 Ok(jvm_field)
242}
243
244fn convert_gaia_global_to_jvm_field(global: &GaiaGlobal) -> Result<JvmField> {
246 let descriptor = convert_gaia_type_to_jvm_descriptor(&global.ty);
247 let field = JvmField::new(global.name.clone(), descriptor).with_public().with_static();
248
249 Ok(field)
250}
251
252fn convert_gaia_instruction_to_jvm(instruction: &GaiaInstruction, ctx: &JvmContext) -> Result<Vec<JvmInstruction>> {
254 match instruction {
255 GaiaInstruction::Core(core) => match core {
256 CoreInstruction::PushConstant(constant) => match constant {
257 GaiaConstant::I8(value) => Ok(vec![JvmInstruction::Bipush { value: *value }]),
258 GaiaConstant::U8(value) => Ok(vec![JvmInstruction::Bipush { value: *value as i8 }]),
259 GaiaConstant::I16(value) => Ok(vec![JvmInstruction::Sipush { value: *value }]),
260 GaiaConstant::U16(value) => Ok(vec![JvmInstruction::Sipush { value: *value as i16 }]),
261 GaiaConstant::I32(value) => match *value {
262 0 => Ok(vec![JvmInstruction::Iconst0]),
263 1 => Ok(vec![JvmInstruction::Iconst1]),
264 2 => Ok(vec![JvmInstruction::Iconst2]),
265 3 => Ok(vec![JvmInstruction::Iconst3]),
266 4 => Ok(vec![JvmInstruction::Iconst4]),
267 5 => Ok(vec![JvmInstruction::Iconst5]),
268 -1 => Ok(vec![JvmInstruction::IconstM1]),
269 _ if *value >= -128 && *value <= 127 => Ok(vec![JvmInstruction::Bipush { value: *value as i8 }]),
270 _ if *value >= -32768 && *value <= 32767 => Ok(vec![JvmInstruction::Sipush { value: *value as i16 }]),
271 _ => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
272 },
273 GaiaConstant::U32(value) => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
274 GaiaConstant::I64(value) => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
275 GaiaConstant::U64(value) => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
276 GaiaConstant::F32(value) => match *value {
277 0.0 => Ok(vec![JvmInstruction::Fconst0]),
278 1.0 => Ok(vec![JvmInstruction::Fconst1]),
279 2.0 => Ok(vec![JvmInstruction::Fconst2]),
280 _ => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
281 },
282 GaiaConstant::F64(value) => match *value {
283 0.0 => Ok(vec![JvmInstruction::Dconst0]),
284 1.0 => Ok(vec![JvmInstruction::Dconst1]),
285 _ => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
286 },
287 GaiaConstant::String(value) => Ok(vec![JvmInstruction::Ldc { symbol: value.clone() }]),
288 GaiaConstant::Bool(value) => Ok(vec![if *value { JvmInstruction::Iconst1 } else { JvmInstruction::Iconst0 }]),
289 GaiaConstant::Null => Ok(vec![JvmInstruction::AconstNull]),
290 _ => Err(GaiaError::custom_error("Unsupported constant type for JVM")),
291 },
292 CoreInstruction::Load(_gaia_type) => {
293 Err(GaiaError::not_implemented("JVM indirect load"))
295 }
296 CoreInstruction::Store(_gaia_type) => {
297 Err(GaiaError::not_implemented("JVM indirect store"))
299 }
300 CoreInstruction::Add(_) => Ok(vec![JvmInstruction::Iadd]),
301 CoreInstruction::Sub(_) => Ok(vec![JvmInstruction::Isub]),
302 CoreInstruction::Mul(_) => Ok(vec![JvmInstruction::Imul]),
303 CoreInstruction::Div(_) => Ok(vec![JvmInstruction::Idiv]),
304 CoreInstruction::Pop => Ok(vec![JvmInstruction::Pop]),
305 CoreInstruction::Dup => Ok(vec![JvmInstruction::Dup]),
306 CoreInstruction::Ret => Ok(vec![JvmInstruction::Return]),
307 CoreInstruction::Br(label) => Ok(vec![JvmInstruction::Goto { target: label.clone() }]),
308 CoreInstruction::BrTrue(label) => Ok(vec![JvmInstruction::Ifne { target: label.clone() }]),
309 CoreInstruction::BrFalse(label) => Ok(vec![JvmInstruction::Ifeq { target: label.clone() }]),
310 CoreInstruction::Label(name) => Ok(vec![JvmInstruction::Label { name: name.clone() }]),
311 CoreInstruction::Call(name, arg_count) => {
312 let jvm_target = gaia_types::helpers::CompilationTarget {
314 build: gaia_types::helpers::Architecture::JVM,
315 host: gaia_types::helpers::AbiCompatible::JavaAssembly,
316 target: gaia_types::helpers::ApiCompatible::JvmRuntime(8),
317 };
318 let mapped = ctx.function_mapper.map_function(&jvm_target, name).unwrap_or(name);
319 Ok(vec![JvmInstruction::Invokestatic {
320 class_name: "Main".to_string(), method_name: mapped.to_string(),
322 descriptor: format!("({})V", "I".repeat(*arg_count)), }])
324 }
325 CoreInstruction::LoadLocal(index, ty) => Ok(vec![match ty {
326 GaiaType::I32
327 | GaiaType::U32
328 | GaiaType::Bool
329 | GaiaType::I8
330 | GaiaType::U8
331 | GaiaType::I16
332 | GaiaType::U16 => JvmInstruction::Iload { index: *index as u16 },
333 GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lload { index: *index as u16 },
334 GaiaType::F32 => JvmInstruction::Fload { index: *index as u16 },
335 GaiaType::F64 => JvmInstruction::Dload { index: *index as u16 },
336 _ => JvmInstruction::Aload { index: *index as u16 },
337 }]),
338 CoreInstruction::StoreLocal(index, ty) => Ok(vec![match ty {
339 GaiaType::I32
340 | GaiaType::U32
341 | GaiaType::Bool
342 | GaiaType::I8
343 | GaiaType::U8
344 | GaiaType::I16
345 | GaiaType::U16 => JvmInstruction::Istore { index: *index as u16 },
346 GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lstore { index: *index as u16 },
347 GaiaType::F32 => JvmInstruction::Fstore { index: *index as u16 },
348 GaiaType::F64 => JvmInstruction::Dstore { index: *index as u16 },
349 _ => JvmInstruction::Astore { index: *index as u16 },
350 }]),
351 CoreInstruction::LoadArg(index, ty) => Ok(vec![match ty {
352 GaiaType::I32
353 | GaiaType::U32
354 | GaiaType::Bool
355 | GaiaType::I8
356 | GaiaType::U8
357 | GaiaType::I16
358 | GaiaType::U16 => JvmInstruction::Iload { index: *index as u16 },
359 GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lload { index: *index as u16 },
360 GaiaType::F32 => JvmInstruction::Fload { index: *index as u16 },
361 GaiaType::F64 => JvmInstruction::Dload { index: *index as u16 },
362 _ => JvmInstruction::Aload { index: *index as u16 },
363 }]),
364 CoreInstruction::StoreArg(index, ty) => Ok(vec![match ty {
365 GaiaType::I32
366 | GaiaType::U32
367 | GaiaType::Bool
368 | GaiaType::I8
369 | GaiaType::U8
370 | GaiaType::I16
371 | GaiaType::U16 => JvmInstruction::Istore { index: *index as u16 },
372 GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lstore { index: *index as u16 },
373 GaiaType::F32 => JvmInstruction::Fstore { index: *index as u16 },
374 GaiaType::F64 => JvmInstruction::Dstore { index: *index as u16 },
375 _ => JvmInstruction::Astore { index: *index as u16 },
376 }]),
377 CoreInstruction::New(type_name) => Ok(vec![
378 JvmInstruction::New { class_name: type_name.replace('.', "/") },
379 JvmInstruction::Dup,
380 JvmInstruction::Invokespecial {
381 class_name: type_name.replace('.', "/"),
382 method_name: "<init>".to_string(),
383 descriptor: "()V".to_string(),
384 },
385 ]),
386 CoreInstruction::LoadField(type_name, field_name) => {
387 let descriptor = ctx
388 .field_types
389 .get(&(type_name.clone(), field_name.clone()))
390 .cloned()
391 .unwrap_or_else(|| "Ljava/lang/Object;".to_string());
392 Ok(vec![JvmInstruction::Getfield {
393 class_name: type_name.replace('.', "/"),
394 field_name: field_name.to_string(),
395 descriptor,
396 }])
397 }
398 CoreInstruction::StoreField(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::Putfield {
405 class_name: type_name.replace('.', "/"),
406 field_name: field_name.to_string(),
407 descriptor,
408 }])
409 }
410 CoreInstruction::LoadElement(ty) => Ok(vec![match ty {
411 GaiaType::I32 | GaiaType::U32 => JvmInstruction::Iaload,
412 GaiaType::I64 | GaiaType::U64 => JvmInstruction::Laload,
413 GaiaType::F32 => JvmInstruction::Faload,
414 GaiaType::F64 => JvmInstruction::Daload,
415 GaiaType::I8 | GaiaType::U8 | GaiaType::Bool => JvmInstruction::Baload,
416 GaiaType::I16 | GaiaType::U16 => JvmInstruction::Saload,
417 _ => JvmInstruction::Aaload,
418 }]),
419 CoreInstruction::StoreElement(ty) => Ok(vec![match ty {
420 GaiaType::I32 | GaiaType::U32 => JvmInstruction::Iastore,
421 GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lastore,
422 GaiaType::F32 => JvmInstruction::Fastore,
423 GaiaType::F64 => JvmInstruction::Dastore,
424 GaiaType::I8 | GaiaType::U8 | GaiaType::Bool => JvmInstruction::Bastore,
425 GaiaType::I16 | GaiaType::U16 => JvmInstruction::Sastore,
426 _ => JvmInstruction::Aastore,
427 }]),
428 CoreInstruction::ArrayLength => Ok(vec![JvmInstruction::Arraylength]),
429 CoreInstruction::NewArray(elem_type, _) => {
430 Ok(vec![match elem_type {
431 GaiaType::I32 | GaiaType::U32 => JvmInstruction::Newarray { atype: 10 }, GaiaType::I64 | GaiaType::U64 => JvmInstruction::Newarray { atype: 11 }, GaiaType::F32 => JvmInstruction::Newarray { atype: 6 }, GaiaType::F64 => JvmInstruction::Newarray { atype: 7 }, GaiaType::I8 | GaiaType::U8 => JvmInstruction::Newarray { atype: 8 }, GaiaType::Bool => JvmInstruction::Newarray { atype: 4 }, GaiaType::I16 | GaiaType::U16 => JvmInstruction::Newarray { atype: 9 }, _ => JvmInstruction::Anewarray { class_name: convert_gaia_type_to_jvm_descriptor(elem_type) },
439 }])
440 }
441 _ => Ok(vec![]),
442 },
443 GaiaInstruction::Managed(managed) => match managed {
444 _ => Ok(vec![]),
445 },
446 _ => Ok(vec![]),
447 }
448}
449
450fn build_method_descriptor(parameters: &[GaiaType], return_type: &Option<GaiaType>) -> String {
452 let mut descriptor = String::from("(");
453
454 for param in parameters {
456 descriptor.push_str(&convert_gaia_type_to_jvm_descriptor(param));
457 }
458
459 descriptor.push(')');
460
461 match return_type {
463 Some(ret_type) => descriptor.push_str(&convert_gaia_type_to_jvm_descriptor(ret_type)),
464 None => descriptor.push('V'), }
466
467 descriptor
468}
469
470fn convert_gaia_type_to_jvm_descriptor(gaia_type: &GaiaType) -> String {
472 match gaia_type {
473 GaiaType::Bool => "Z".to_string(),
474 GaiaType::I8 | GaiaType::U8 => "B".to_string(),
475 GaiaType::I16 | GaiaType::U16 => "S".to_string(),
476 GaiaType::I32 | GaiaType::U32 => "I".to_string(),
477 GaiaType::I64 | GaiaType::U64 => "J".to_string(),
478 GaiaType::F32 | GaiaType::F16 => "F".to_string(),
479 GaiaType::F64 => "D".to_string(),
480 GaiaType::String => "Ljava/lang/String;".to_string(),
481 GaiaType::Class(name) => format!("L{};", name.replace('.', "/")),
482 GaiaType::Struct(name) => format!("L{};", name.replace('.', "/")),
483 GaiaType::Array(elem, _) => format!("[{}", convert_gaia_type_to_jvm_descriptor(elem)),
484 GaiaType::Void => "V".to_string(),
485 GaiaType::Object => "Ljava/lang/Object;".to_string(),
486 _ => "Ljava/lang/Object;".to_string(),
487 }
488}