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, 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#[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 AbiCompatible::Unknown => 80.0,
52 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 let jvm_program = convert_gaia_to_jvm(program, config)?;
67
68 match config.target.host {
69 AbiCompatible::Unknown => {
70 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 pub fn generate_program(program: &GaiaModule) -> Result<JvmProgram> {
96 #[cfg(feature = "jvm-assembler")]
97 {
98 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")]
111struct JvmContext {
113 function_mapper: FunctionMapper,
114 field_types: HashMap<(String, String), String>,
116}
117
118#[cfg(feature = "jvm-assembler")]
119fn convert_gaia_to_jvm(program: &GaiaModule, config: &GaiaConfig) -> Result<JvmProgram> {
121 let mut jvm_program = JvmProgram::new(program.name.clone());
122
123 jvm_program.version = JvmVersion { major: 52, minor: 0 }; jvm_program.access_flags = JvmAccessFlags::public();
128
129 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 let ctx = JvmContext { function_mapper: FunctionMapper::from_config(&config.setting)?, field_types };
142
143 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 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 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")]
171fn convert_gaia_function_to_jvm(function: &GaiaFunction, ctx: &JvmContext) -> Result<JvmMethod> {
173 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 method.access_flags.is_public = true;
180 method.access_flags.is_static = true;
181
182 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 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 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 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(), });
218 method.add_instruction(JvmInstruction::Goto { target: next_block.clone() });
219 }
220 crate::program::GaiaTerminator::Halt => {
221 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 method.max_stack = 10;
234 method.max_locals = 10;
235
236 Ok(method)
237}
238
239#[cfg(feature = "jvm-assembler")]
240fn 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")]
260fn 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")]
271fn 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")]
447fn 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")]
471fn 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}