1use crate::lcnf::*;
6use std::collections::HashSet;
7
8use super::types::{
9 JavaAnalysisCache, JavaBackend, JavaClass, JavaConstantFoldingHelper, JavaDepGraph,
10 JavaDominatorTree, JavaEnum, JavaExpr, JavaField, JavaLit, JavaLivenessInfo, JavaMethod,
11 JavaModule, JavaPassConfig, JavaPassPhase, JavaPassRegistry, JavaPassStats, JavaRecord,
12 JavaStmt, JavaType, JavaWorklist, SealedInterface, Visibility,
13};
14
15pub(super) fn boxed_to_ref(ty: &JavaType) -> std::string::String {
17 match ty {
18 JavaType::Int => "Integer".to_string(),
19 JavaType::Long => "Long".to_string(),
20 JavaType::Double => "Double".to_string(),
21 JavaType::Float => "Float".to_string(),
22 JavaType::Boolean => "Boolean".to_string(),
23 JavaType::Char => "Character".to_string(),
24 JavaType::Byte => "Byte".to_string(),
25 JavaType::Short => "Short".to_string(),
26 _ => ty.to_string(),
27 }
28}
29pub(super) fn lcnf_type_to_java(ty: &LcnfType) -> JavaType {
31 match ty {
32 LcnfType::Nat => JavaType::Long,
33 LcnfType::LcnfString => JavaType::String,
34 LcnfType::Unit | LcnfType::Erased | LcnfType::Irrelevant => JavaType::Void,
35 LcnfType::Object => JavaType::Object,
36 LcnfType::Var(name) => JavaType::Custom(name.clone()),
37 LcnfType::Fun(params, ret) => {
38 if params.is_empty() {
39 JavaType::Generic("Supplier".to_string(), vec![lcnf_type_to_java(ret)])
40 } else if params.len() == 1 {
41 JavaType::Generic(
42 "Function".to_string(),
43 vec![lcnf_type_to_java(¶ms[0]), lcnf_type_to_java(ret)],
44 )
45 } else {
46 JavaType::Custom("Object".to_string())
47 }
48 }
49 LcnfType::Ctor(name, _args) => JavaType::Custom(name.clone()),
50 }
51}
52pub(super) fn indent(level: usize) -> std::string::String {
53 " ".repeat(level)
54}
55pub(super) fn emit_annotations(
56 out: &mut std::string::String,
57 annotations: &[std::string::String],
58 level: usize,
59) {
60 for ann in annotations {
61 out.push_str(&format!("{}{}\n", indent(level), ann));
62 }
63}
64pub(super) fn emit_sealed_interface(
65 out: &mut std::string::String,
66 iface: &SealedInterface,
67 level: usize,
68) {
69 emit_annotations(out, &iface.annotations, level);
70 let ind = indent(level);
71 out.push_str(&format!("{}public sealed interface {}", ind, iface.name));
72 if !iface.extends.is_empty() {
73 out.push_str(" extends ");
74 out.push_str(&iface.extends.join(", "));
75 }
76 if !iface.permits.is_empty() {
77 out.push_str(" permits ");
78 out.push_str(&iface.permits.join(", "));
79 }
80 out.push_str(" {\n");
81 for method in &iface.methods {
82 emit_method(out, method, level + 1, true);
83 }
84 out.push_str(&format!("{}}}\n", ind));
85}
86pub(super) fn emit_record(out: &mut std::string::String, rec: &JavaRecord, level: usize) {
87 emit_annotations(out, &rec.annotations, level);
88 let ind = indent(level);
89 out.push_str(&format!("{}public record {}", ind, rec.name));
90 out.push('(');
91 for (i, (name, ty)) in rec.components.iter().enumerate() {
92 if i > 0 {
93 out.push_str(", ");
94 }
95 out.push_str(&format!("{} {}", ty, name));
96 }
97 out.push(')');
98 if !rec.implements.is_empty() {
99 out.push_str(" implements ");
100 out.push_str(&rec.implements.join(", "));
101 }
102 if rec.methods.is_empty() {
103 out.push_str(" {}\n");
104 } else {
105 out.push_str(" {\n");
106 for method in &rec.methods {
107 emit_method(out, method, level + 1, false);
108 }
109 out.push_str(&format!("{}}}\n", ind));
110 }
111}
112pub(super) fn emit_enum(out: &mut std::string::String, en: &JavaEnum, level: usize) {
113 emit_annotations(out, &en.annotations, level);
114 let ind = indent(level);
115 let vis = match en.visibility {
116 Visibility::Package => std::string::String::new(),
117 ref v => format!("{} ", v),
118 };
119 out.push_str(&format!("{}{}enum {}", ind, vis, en.name));
120 if !en.interfaces.is_empty() {
121 out.push_str(" implements ");
122 out.push_str(&en.interfaces.join(", "));
123 }
124 out.push_str(" {\n");
125 for (i, constant) in en.constants.iter().enumerate() {
126 emit_annotations(out, &constant.annotations, level + 1);
127 out.push_str(&format!("{}{}", indent(level + 1), constant.name));
128 if !constant.args.is_empty() {
129 out.push('(');
130 for (j, arg) in constant.args.iter().enumerate() {
131 if j > 0 {
132 out.push_str(", ");
133 }
134 out.push_str(&format!("{}", arg));
135 }
136 out.push(')');
137 }
138 if i + 1 < en.constants.len() {
139 out.push(',');
140 } else {
141 out.push(';');
142 }
143 out.push('\n');
144 }
145 if !en.fields.is_empty() || !en.methods.is_empty() {
146 out.push('\n');
147 for field in &en.fields {
148 emit_field(out, field, level + 1);
149 }
150 for method in &en.methods {
151 emit_method(out, method, level + 1, false);
152 }
153 }
154 out.push_str(&format!("{}}}\n", ind));
155}
156pub(super) fn emit_class(out: &mut std::string::String, cls: &JavaClass, level: usize) {
157 emit_annotations(out, &cls.annotations, level);
158 let ind = indent(level);
159 let vis = match cls.visibility {
160 Visibility::Package => std::string::String::new(),
161 ref v => format!("{} ", v),
162 };
163 out.push_str(&format!("{}{}", ind, vis));
164 for m in &cls.modifiers {
165 out.push_str(&format!("{} ", m));
166 }
167 out.push_str(&format!("class {}", cls.name));
168 if !cls.type_params.is_empty() {
169 out.push('<');
170 out.push_str(&cls.type_params.join(", "));
171 out.push('>');
172 }
173 if let Some(sup) = &cls.superclass {
174 out.push_str(&format!(" extends {}", sup));
175 }
176 if !cls.interfaces.is_empty() {
177 out.push_str(" implements ");
178 out.push_str(&cls.interfaces.join(", "));
179 }
180 if !cls.permits.is_empty() {
181 out.push_str(" permits ");
182 out.push_str(&cls.permits.join(", "));
183 }
184 out.push_str(" {\n");
185 for field in &cls.fields {
186 emit_field(out, field, level + 1);
187 }
188 if !cls.fields.is_empty() {
189 out.push('\n');
190 }
191 for method in &cls.methods {
192 emit_method(out, method, level + 1, false);
193 }
194 for inner in &cls.inner_classes {
195 out.push('\n');
196 emit_class(out, inner, level + 1);
197 }
198 out.push_str(&format!("{}}}\n", ind));
199}
200pub(super) fn emit_field(out: &mut std::string::String, field: &JavaField, level: usize) {
201 emit_annotations(out, &field.annotations, level);
202 let ind = indent(level);
203 let vis = match field.visibility {
204 Visibility::Package => std::string::String::new(),
205 ref v => format!("{} ", v),
206 };
207 let static_kw = if field.is_static { "static " } else { "" };
208 let final_kw = if field.is_final { "final " } else { "" };
209 if let Some(init) = &field.init {
210 out.push_str(&format!(
211 "{}{}{}{}{} {} = {};\n",
212 ind, vis, static_kw, final_kw, field.ty, field.name, init
213 ));
214 } else {
215 out.push_str(&format!(
216 "{}{}{}{}{} {};\n",
217 ind, vis, static_kw, final_kw, field.ty, field.name
218 ));
219 }
220}
221pub(super) fn emit_method(
222 out: &mut std::string::String,
223 method: &JavaMethod,
224 level: usize,
225 in_interface: bool,
226) {
227 emit_annotations(out, &method.annotations, level);
228 let ind = indent(level);
229 let vis = match method.visibility {
230 Visibility::Package => std::string::String::new(),
231 ref v => format!("{} ", v),
232 };
233 let static_kw = if method.is_static { "static " } else { "" };
234 let final_kw = if method.is_final && !in_interface {
235 "final "
236 } else {
237 ""
238 };
239 let abstract_kw = if method.is_abstract { "abstract " } else { "" };
240 out.push_str(&format!(
241 "{}{}{}{}{}{} {}(",
242 ind, vis, static_kw, final_kw, abstract_kw, method.return_type, method.name
243 ));
244 for (i, (pname, pty)) in method.params.iter().enumerate() {
245 if i > 0 {
246 out.push_str(", ");
247 }
248 out.push_str(&format!("{} {}", pty, pname));
249 }
250 out.push(')');
251 if !method.throws.is_empty() {
252 out.push_str(" throws ");
253 out.push_str(&method.throws.join(", "));
254 }
255 if method.is_abstract || (in_interface && method.body.is_empty()) {
256 out.push_str(";\n");
257 return;
258 }
259 out.push_str(" {\n");
260 for stmt in &method.body {
261 emit_stmt(out, stmt, level + 1);
262 }
263 out.push_str(&format!("{}}}\n", ind));
264}
265pub(super) fn emit_stmt(out: &mut std::string::String, stmt: &JavaStmt, level: usize) {
266 let ind = indent(level);
267 match stmt {
268 JavaStmt::Expr(expr) => {
269 out.push_str(&format!("{}{};\n", ind, expr));
270 }
271 JavaStmt::LocalVar {
272 ty,
273 name,
274 init,
275 is_final,
276 } => {
277 let final_kw = if *is_final { "final " } else { "" };
278 let type_str = match ty {
279 Some(t) => format!("{}", t),
280 None => "var".to_string(),
281 };
282 match init {
283 Some(expr) => {
284 out.push_str(&format!(
285 "{}{}{} {} = {};\n",
286 ind, final_kw, type_str, name, expr
287 ));
288 }
289 None => {
290 out.push_str(&format!("{}{}{} {};\n", ind, final_kw, type_str, name));
291 }
292 }
293 }
294 JavaStmt::If(cond, then_body, else_body) => {
295 out.push_str(&format!("{}if ({}) {{\n", ind, cond));
296 for s in then_body {
297 emit_stmt(out, s, level + 1);
298 }
299 if else_body.is_empty() {
300 out.push_str(&format!("{}}}\n", ind));
301 } else {
302 out.push_str(&format!("{}}} else {{\n", ind));
303 for s in else_body {
304 emit_stmt(out, s, level + 1);
305 }
306 out.push_str(&format!("{}}}\n", ind));
307 }
308 }
309 JavaStmt::Switch {
310 scrutinee,
311 cases,
312 default,
313 } => {
314 out.push_str(&format!("{}switch ({}) {{\n", ind, scrutinee));
315 for (label, body) in cases {
316 out.push_str(&format!("{} case {} -> {{\n", ind, label));
317 for s in body {
318 emit_stmt(out, s, level + 2);
319 }
320 out.push_str(&format!("{} }}\n", ind));
321 }
322 if !default.is_empty() {
323 out.push_str(&format!("{} default -> {{\n", ind));
324 for s in default {
325 emit_stmt(out, s, level + 2);
326 }
327 out.push_str(&format!("{} }}\n", ind));
328 }
329 out.push_str(&format!("{}}}\n", ind));
330 }
331 JavaStmt::For {
332 init,
333 cond,
334 update,
335 body,
336 } => {
337 let init_str = match init {
338 Some(s) => {
339 let mut tmp = std::string::String::new();
340 emit_stmt(&mut tmp, s, 0);
341 tmp.trim_end_matches(";\n").trim().to_string()
342 }
343 None => std::string::String::new(),
344 };
345 let cond_str = match cond {
346 Some(c) => format!("{}", c),
347 None => std::string::String::new(),
348 };
349 let update_str = match update {
350 Some(u) => format!("{}", u),
351 None => std::string::String::new(),
352 };
353 out.push_str(&format!(
354 "{}for ({}; {}; {}) {{\n",
355 ind, init_str, cond_str, update_str
356 ));
357 for s in body {
358 emit_stmt(out, s, level + 1);
359 }
360 out.push_str(&format!("{}}}\n", ind));
361 }
362 JavaStmt::ForEach {
363 ty,
364 elem,
365 iterable,
366 body,
367 } => {
368 out.push_str(&format!("{}for ({} {} : {}) {{\n", ind, ty, elem, iterable));
369 for s in body {
370 emit_stmt(out, s, level + 1);
371 }
372 out.push_str(&format!("{}}}\n", ind));
373 }
374 JavaStmt::While(cond, body) => {
375 out.push_str(&format!("{}while ({}) {{\n", ind, cond));
376 for s in body {
377 emit_stmt(out, s, level + 1);
378 }
379 out.push_str(&format!("{}}}\n", ind));
380 }
381 JavaStmt::DoWhile(body, cond) => {
382 out.push_str(&format!("{}do {{\n", ind));
383 for s in body {
384 emit_stmt(out, s, level + 1);
385 }
386 out.push_str(&format!("{}}} while ({});\n", ind, cond));
387 }
388 JavaStmt::Return(Some(expr)) => {
389 out.push_str(&format!("{}return {};\n", ind, expr));
390 }
391 JavaStmt::Return(None) => {
392 out.push_str(&format!("{}return;\n", ind));
393 }
394 JavaStmt::Throw(expr) => {
395 out.push_str(&format!("{}throw {};\n", ind, expr));
396 }
397 JavaStmt::TryCatch {
398 body,
399 catches,
400 finally,
401 } => {
402 out.push_str(&format!("{}try {{\n", ind));
403 for s in body {
404 emit_stmt(out, s, level + 1);
405 }
406 for catch in catches {
407 let exc_str = catch.exception_types.join(" | ");
408 out.push_str(&format!(
409 "{}}} catch ({} {}) {{\n",
410 ind, exc_str, catch.var_name
411 ));
412 for s in &catch.body {
413 emit_stmt(out, s, level + 1);
414 }
415 }
416 if !finally.is_empty() {
417 out.push_str(&format!("{}}} finally {{\n", ind));
418 for s in finally {
419 emit_stmt(out, s, level + 1);
420 }
421 }
422 out.push_str(&format!("{}}}\n", ind));
423 }
424 JavaStmt::TryWithResources {
425 resources,
426 body,
427 catches,
428 finally,
429 } => {
430 out.push_str(&format!("{}try (", ind));
431 for (i, (name, expr)) in resources.iter().enumerate() {
432 if i > 0 {
433 out.push_str("; ");
434 }
435 out.push_str(&format!("var {} = {}", name, expr));
436 }
437 out.push_str(") {\n");
438 for s in body {
439 emit_stmt(out, s, level + 1);
440 }
441 for catch in catches {
442 let exc_str = catch.exception_types.join(" | ");
443 out.push_str(&format!(
444 "{}}} catch ({} {}) {{\n",
445 ind, exc_str, catch.var_name
446 ));
447 for s in &catch.body {
448 emit_stmt(out, s, level + 1);
449 }
450 }
451 if !finally.is_empty() {
452 out.push_str(&format!("{}}} finally {{\n", ind));
453 for s in finally {
454 emit_stmt(out, s, level + 1);
455 }
456 }
457 out.push_str(&format!("{}}}\n", ind));
458 }
459 JavaStmt::Synchronized(lock, body) => {
460 out.push_str(&format!("{}synchronized ({}) {{\n", ind, lock));
461 for s in body {
462 emit_stmt(out, s, level + 1);
463 }
464 out.push_str(&format!("{}}}\n", ind));
465 }
466 JavaStmt::Break(label) => match label {
467 Some(l) => out.push_str(&format!("{}break {};\n", ind, l)),
468 None => out.push_str(&format!("{}break;\n", ind)),
469 },
470 JavaStmt::Continue(label) => match label {
471 Some(l) => out.push_str(&format!("{}continue {};\n", ind, l)),
472 None => out.push_str(&format!("{}continue;\n", ind)),
473 },
474 JavaStmt::Assert(cond, msg) => match msg {
475 Some(m) => out.push_str(&format!("{}assert {} : {};\n", ind, cond, m)),
476 None => out.push_str(&format!("{}assert {};\n", ind, cond)),
477 },
478 }
479}
480pub const JAVA_KEYWORDS: &[&str] = &[
482 "abstract",
483 "assert",
484 "boolean",
485 "break",
486 "byte",
487 "case",
488 "catch",
489 "char",
490 "class",
491 "const",
492 "continue",
493 "default",
494 "do",
495 "double",
496 "else",
497 "enum",
498 "extends",
499 "final",
500 "finally",
501 "float",
502 "for",
503 "goto",
504 "if",
505 "implements",
506 "import",
507 "instanceof",
508 "int",
509 "interface",
510 "long",
511 "native",
512 "new",
513 "package",
514 "private",
515 "protected",
516 "public",
517 "return",
518 "short",
519 "static",
520 "strictfp",
521 "super",
522 "switch",
523 "synchronized",
524 "this",
525 "throw",
526 "throws",
527 "transient",
528 "try",
529 "void",
530 "volatile",
531 "while",
532 "true",
533 "false",
534 "null",
535 "var",
536 "record",
537 "sealed",
538 "permits",
539 "yield",
540 "when",
541];
542pub(super) fn collect_ctor_names_from_expr(
543 expr: &LcnfExpr,
544 out: &mut HashSet<std::string::String>,
545) {
546 match expr {
547 LcnfExpr::Let { value, body, .. } => {
548 collect_ctor_names_from_value(value, out);
549 collect_ctor_names_from_expr(body, out);
550 }
551 LcnfExpr::Case { alts, default, .. } => {
552 for alt in alts {
553 out.insert(alt.ctor_name.clone());
554 collect_ctor_names_from_expr(&alt.body, out);
555 }
556 if let Some(d) = default {
557 collect_ctor_names_from_expr(d, out);
558 }
559 }
560 LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(_, _) => {}
561 }
562}
563pub(super) fn collect_ctor_names_from_value(
564 value: &LcnfLetValue,
565 out: &mut HashSet<std::string::String>,
566) {
567 match value {
568 LcnfLetValue::Ctor(name, _, _) => {
569 out.insert(name.clone());
570 }
571 LcnfLetValue::Reuse(_, name, _, _) => {
572 out.insert(name.clone());
573 }
574 _ => {}
575 }
576}
577pub const JAVA_RUNTIME: &str = r#"
579/**
580 * OxiLean Java Runtime — generated, do not modify.
581 */
582public final class OxiLeanRuntime {
583
584 private OxiLeanRuntime() {}
585
586 /** Called when pattern matching reaches an unreachable branch. */
587 public static RuntimeException unreachable() {
588 throw new IllegalStateException("OxiLean: unreachable code reached");
589 }
590
591 /** Saturating natural-number subtraction (truncates at 0). */
592 public static long natSub(long a, long b) {
593 return Math.max(0L, a - b);
594 }
595
596 /** Natural-number division (returns 0 on division by zero). */
597 public static long natDiv(long a, long b) {
598 return b == 0L ? 0L : a / b;
599 }
600
601 /** Natural-number modulo (returns a on division by zero). */
602 public static long natMod(long a, long b) {
603 return b == 0L ? a : a % b;
604 }
605
606 /** Boolean to Nat conversion. */
607 public static long decide(boolean b) {
608 return b ? 1L : 0L;
609 }
610
611 /** String representation of a Nat. */
612 public static String natToString(long n) {
613 return Long.toString(n);
614 }
615
616 /** String append. */
617 public static String strAppend(String a, String b) {
618 return a + b;
619 }
620
621 /** Pair (generic tuple). */
622 public record Pair<A, B>(A fst, B snd) {}
623
624 /** Pair constructor. */
625 public static <A, B> Pair<A, B> mkPair(A a, B b) {
626 return new Pair<>(a, b);
627 }
628}
629"#;
630#[cfg(test)]
631mod tests {
632 use super::*;
633 #[test]
634 pub(super) fn test_java_type_primitives() {
635 assert_eq!(JavaType::Int.to_string(), "int");
636 assert_eq!(JavaType::Long.to_string(), "long");
637 assert_eq!(JavaType::Double.to_string(), "double");
638 assert_eq!(JavaType::Float.to_string(), "float");
639 assert_eq!(JavaType::Boolean.to_string(), "boolean");
640 assert_eq!(JavaType::Char.to_string(), "char");
641 assert_eq!(JavaType::Byte.to_string(), "byte");
642 assert_eq!(JavaType::Short.to_string(), "short");
643 assert_eq!(JavaType::Void.to_string(), "void");
644 assert_eq!(JavaType::String.to_string(), "String");
645 assert_eq!(JavaType::Object.to_string(), "Object");
646 }
647 #[test]
648 pub(super) fn test_java_type_array() {
649 let t = JavaType::Array(Box::new(JavaType::Int));
650 assert_eq!(t.to_string(), "int[]");
651 }
652 #[test]
653 pub(super) fn test_java_type_list() {
654 let t = JavaType::List(Box::new(JavaType::String));
655 assert_eq!(t.to_string(), "List<String>");
656 }
657 #[test]
658 pub(super) fn test_java_type_list_primitive_boxed() {
659 let t = JavaType::List(Box::new(JavaType::Int));
660 assert_eq!(t.to_string(), "List<Integer>");
661 }
662 #[test]
663 pub(super) fn test_java_type_map() {
664 let t = JavaType::Map(Box::new(JavaType::String), Box::new(JavaType::Int));
665 assert_eq!(t.to_string(), "Map<String, Integer>");
666 }
667 #[test]
668 pub(super) fn test_java_type_optional() {
669 let t = JavaType::Optional(Box::new(JavaType::String));
670 assert_eq!(t.to_string(), "Optional<String>");
671 }
672 #[test]
673 pub(super) fn test_java_type_custom() {
674 let t = JavaType::Custom("MyClass".to_string());
675 assert_eq!(t.to_string(), "MyClass");
676 }
677 #[test]
678 pub(super) fn test_java_type_generic() {
679 let t = JavaType::Generic("Map".to_string(), vec![JavaType::String, JavaType::Long]);
680 assert_eq!(t.to_string(), "Map<String, long>");
681 }
682 #[test]
683 pub(super) fn test_java_lit_int() {
684 assert_eq!(JavaLit::Int(42).to_string(), "42");
685 assert_eq!(JavaLit::Int(-7).to_string(), "-7");
686 }
687 #[test]
688 pub(super) fn test_java_lit_long() {
689 assert_eq!(JavaLit::Long(100).to_string(), "100L");
690 }
691 #[test]
692 pub(super) fn test_java_lit_bool() {
693 assert_eq!(JavaLit::Bool(true).to_string(), "true");
694 assert_eq!(JavaLit::Bool(false).to_string(), "false");
695 }
696 #[test]
697 pub(super) fn test_java_lit_null() {
698 assert_eq!(JavaLit::Null.to_string(), "null");
699 }
700 #[test]
701 pub(super) fn test_java_lit_string_escaping() {
702 let s = JavaLit::Str("hello\nworld\"test".to_string());
703 assert_eq!(s.to_string(), r#""hello\nworld\"test""#);
704 }
705 #[test]
706 pub(super) fn test_java_lit_char() {
707 assert_eq!(JavaLit::Char('a').to_string(), "'a'");
708 }
709 #[test]
710 pub(super) fn test_java_expr_var() {
711 assert_eq!(JavaExpr::Var("x".to_string()).to_string(), "x");
712 }
713 #[test]
714 pub(super) fn test_java_expr_binop() {
715 let e = JavaExpr::BinOp(
716 "+".to_string(),
717 Box::new(JavaExpr::Var("a".to_string())),
718 Box::new(JavaExpr::Var("b".to_string())),
719 );
720 assert_eq!(e.to_string(), "(a + b)");
721 }
722 #[test]
723 pub(super) fn test_java_expr_method_call() {
724 let e = JavaExpr::MethodCall(
725 Box::new(JavaExpr::Var("list".to_string())),
726 "stream".to_string(),
727 vec![],
728 );
729 assert_eq!(e.to_string(), "list.stream()");
730 }
731 #[test]
732 pub(super) fn test_java_expr_new() {
733 let e = JavaExpr::New("ArrayList".to_string(), vec![]);
734 assert_eq!(e.to_string(), "new ArrayList()");
735 }
736 #[test]
737 pub(super) fn test_java_expr_lambda_single_param() {
738 let e = JavaExpr::Lambda(
739 vec!["x".to_string()],
740 Box::new(JavaExpr::BinOp(
741 ">".to_string(),
742 Box::new(JavaExpr::Var("x".to_string())),
743 Box::new(JavaExpr::Lit(JavaLit::Int(0))),
744 )),
745 );
746 assert_eq!(e.to_string(), "x -> (x > 0)");
747 }
748 #[test]
749 pub(super) fn test_java_expr_lambda_no_params() {
750 let e = JavaExpr::Lambda(vec![], Box::new(JavaExpr::Lit(JavaLit::Int(42))));
751 assert_eq!(e.to_string(), "() -> 42");
752 }
753 #[test]
754 pub(super) fn test_java_expr_lambda_multi_param() {
755 let e = JavaExpr::Lambda(
756 vec!["x".to_string(), "y".to_string()],
757 Box::new(JavaExpr::BinOp(
758 "+".to_string(),
759 Box::new(JavaExpr::Var("x".to_string())),
760 Box::new(JavaExpr::Var("y".to_string())),
761 )),
762 );
763 assert_eq!(e.to_string(), "(x, y) -> (x + y)");
764 }
765 #[test]
766 pub(super) fn test_java_expr_ternary() {
767 let e = JavaExpr::Ternary(
768 Box::new(JavaExpr::Var("cond".to_string())),
769 Box::new(JavaExpr::Lit(JavaLit::Int(1))),
770 Box::new(JavaExpr::Lit(JavaLit::Int(0))),
771 );
772 assert_eq!(e.to_string(), "(cond ? 1 : 0)");
773 }
774 #[test]
775 pub(super) fn test_java_expr_method_ref() {
776 let e = JavaExpr::MethodRef("String".to_string(), "valueOf".to_string());
777 assert_eq!(e.to_string(), "String::valueOf");
778 }
779 #[test]
780 pub(super) fn test_java_expr_instanceof() {
781 let e = JavaExpr::Instanceof(
782 Box::new(JavaExpr::Var("obj".to_string())),
783 "String".to_string(),
784 );
785 assert_eq!(e.to_string(), "(obj instanceof String)");
786 }
787 #[test]
788 pub(super) fn test_java_expr_cast() {
789 let e = JavaExpr::Cast(JavaType::Long, Box::new(JavaExpr::Var("n".to_string())));
790 assert_eq!(e.to_string(), "((long) n)");
791 }
792 #[test]
793 pub(super) fn test_java_expr_array_access() {
794 let e = JavaExpr::ArrayAccess(
795 Box::new(JavaExpr::Var("arr".to_string())),
796 Box::new(JavaExpr::Lit(JavaLit::Int(0))),
797 );
798 assert_eq!(e.to_string(), "arr[0]");
799 }
800 #[test]
801 pub(super) fn test_emit_simple_record() {
802 let rec = JavaRecord::new("Point", vec![("x", JavaType::Int), ("y", JavaType::Int)]);
803 let mut out = std::string::String::new();
804 emit_record(&mut out, &rec, 0);
805 assert!(out.contains("record Point"));
806 assert!(out.contains("int x"));
807 assert!(out.contains("int y"));
808 }
809 #[test]
810 pub(super) fn test_emit_record_with_implements() {
811 let mut rec = JavaRecord::new("Lit", vec![("value", JavaType::Int)]);
812 rec.implements.push("Expr".to_string());
813 let mut out = std::string::String::new();
814 emit_record(&mut out, &rec, 0);
815 assert!(out.contains("implements Expr"));
816 }
817 #[test]
818 pub(super) fn test_emit_sealed_interface() {
819 let iface = SealedInterface::new("Expr", vec!["Lit", "Add", "Mul"]);
820 let mut out = std::string::String::new();
821 emit_sealed_interface(&mut out, &iface, 0);
822 assert!(out.contains("sealed interface Expr"));
823 assert!(out.contains("permits Lit, Add, Mul"));
824 }
825 #[test]
826 pub(super) fn test_emit_simple_enum() {
827 let en = JavaEnum::new("Color", vec!["RED", "GREEN", "BLUE"]);
828 let mut out = std::string::String::new();
829 emit_enum(&mut out, &en, 0);
830 assert!(out.contains("enum Color"));
831 assert!(out.contains("RED,"));
832 assert!(out.contains("GREEN,"));
833 assert!(out.contains("BLUE;"));
834 }
835 #[test]
836 pub(super) fn test_emit_simple_class() {
837 let cls = JavaClass::new("Foo");
838 let mut out = std::string::String::new();
839 emit_class(&mut out, &cls, 0);
840 assert!(out.contains("public class Foo"));
841 assert!(out.contains('{'));
842 assert!(out.contains('}'));
843 }
844 #[test]
845 pub(super) fn test_emit_class_with_superclass() {
846 let mut cls = JavaClass::new("Bar");
847 cls.superclass = Some("Foo".to_string());
848 let mut out = std::string::String::new();
849 emit_class(&mut out, &cls, 0);
850 assert!(out.contains("extends Foo"));
851 }
852 #[test]
853 pub(super) fn test_module_emit_package() {
854 let module = JavaModule::new("com.example");
855 let src = module.emit();
856 assert!(src.starts_with("package com.example;"));
857 }
858 #[test]
859 pub(super) fn test_module_emit_imports() {
860 let mut module = JavaModule::new("com.example");
861 module.imports.push("java.util.List".to_string());
862 let src = module.emit();
863 assert!(src.contains("import java.util.List;"));
864 }
865 #[test]
866 pub(super) fn test_mangle_reserved_keyword() {
867 let backend = JavaBackend::new();
868 assert_eq!(backend.mangle_name("class"), "class_");
869 assert_eq!(backend.mangle_name("int"), "int_");
870 assert_eq!(backend.mangle_name("return"), "return_");
871 }
872 #[test]
873 pub(super) fn test_mangle_digit_start() {
874 let backend = JavaBackend::new();
875 assert_eq!(backend.mangle_name("1foo"), "_1foo");
876 }
877 #[test]
878 pub(super) fn test_mangle_special_chars() {
879 let backend = JavaBackend::new();
880 assert_eq!(backend.mangle_name("foo.bar"), "foo_bar");
881 assert_eq!(backend.mangle_name("foo::bar"), "foo__bar");
882 }
883 #[test]
884 pub(super) fn test_mangle_empty() {
885 let backend = JavaBackend::new();
886 assert_eq!(backend.mangle_name(""), "_anon");
887 }
888 #[test]
889 pub(super) fn test_visibility_display() {
890 assert_eq!(Visibility::Public.to_string(), "public");
891 assert_eq!(Visibility::Protected.to_string(), "protected");
892 assert_eq!(Visibility::Private.to_string(), "private");
893 assert_eq!(Visibility::Package.to_string(), "");
894 }
895 #[test]
896 pub(super) fn test_java_runtime_content() {
897 assert!(JAVA_RUNTIME.contains("OxiLeanRuntime"));
898 assert!(JAVA_RUNTIME.contains("natSub"));
899 assert!(JAVA_RUNTIME.contains("natDiv"));
900 assert!(JAVA_RUNTIME.contains("strAppend"));
901 }
902}
903#[cfg(test)]
904mod Java_infra_tests {
905 use super::*;
906 #[test]
907 pub(super) fn test_pass_config() {
908 let config = JavaPassConfig::new("test_pass", JavaPassPhase::Transformation);
909 assert!(config.enabled);
910 assert!(config.phase.is_modifying());
911 assert_eq!(config.phase.name(), "transformation");
912 }
913 #[test]
914 pub(super) fn test_pass_stats() {
915 let mut stats = JavaPassStats::new();
916 stats.record_run(10, 100, 3);
917 stats.record_run(20, 200, 5);
918 assert_eq!(stats.total_runs, 2);
919 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
920 assert!((stats.success_rate() - 1.0).abs() < 0.01);
921 let s = stats.format_summary();
922 assert!(s.contains("Runs: 2/2"));
923 }
924 #[test]
925 pub(super) fn test_pass_registry() {
926 let mut reg = JavaPassRegistry::new();
927 reg.register(JavaPassConfig::new("pass_a", JavaPassPhase::Analysis));
928 reg.register(JavaPassConfig::new("pass_b", JavaPassPhase::Transformation).disabled());
929 assert_eq!(reg.total_passes(), 2);
930 assert_eq!(reg.enabled_count(), 1);
931 reg.update_stats("pass_a", 5, 50, 2);
932 let stats = reg.get_stats("pass_a").expect("stats should exist");
933 assert_eq!(stats.total_changes, 5);
934 }
935 #[test]
936 pub(super) fn test_analysis_cache() {
937 let mut cache = JavaAnalysisCache::new(10);
938 cache.insert("key1".to_string(), vec![1, 2, 3]);
939 assert!(cache.get("key1").is_some());
940 assert!(cache.get("key2").is_none());
941 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
942 cache.invalidate("key1");
943 assert!(!cache.entries["key1"].valid);
944 assert_eq!(cache.size(), 1);
945 }
946 #[test]
947 pub(super) fn test_worklist() {
948 let mut wl = JavaWorklist::new();
949 assert!(wl.push(1));
950 assert!(wl.push(2));
951 assert!(!wl.push(1));
952 assert_eq!(wl.len(), 2);
953 assert_eq!(wl.pop(), Some(1));
954 assert!(!wl.contains(1));
955 assert!(wl.contains(2));
956 }
957 #[test]
958 pub(super) fn test_dominator_tree() {
959 let mut dt = JavaDominatorTree::new(5);
960 dt.set_idom(1, 0);
961 dt.set_idom(2, 0);
962 dt.set_idom(3, 1);
963 assert!(dt.dominates(0, 3));
964 assert!(dt.dominates(1, 3));
965 assert!(!dt.dominates(2, 3));
966 assert!(dt.dominates(3, 3));
967 }
968 #[test]
969 pub(super) fn test_liveness() {
970 let mut liveness = JavaLivenessInfo::new(3);
971 liveness.add_def(0, 1);
972 liveness.add_use(1, 1);
973 assert!(liveness.defs[0].contains(&1));
974 assert!(liveness.uses[1].contains(&1));
975 }
976 #[test]
977 pub(super) fn test_constant_folding() {
978 assert_eq!(JavaConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
979 assert_eq!(JavaConstantFoldingHelper::fold_div_i64(10, 0), None);
980 assert_eq!(JavaConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
981 assert_eq!(
982 JavaConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
983 0b1000
984 );
985 assert_eq!(JavaConstantFoldingHelper::fold_bitnot_i64(0), -1);
986 }
987 #[test]
988 pub(super) fn test_dep_graph() {
989 let mut g = JavaDepGraph::new();
990 g.add_dep(1, 2);
991 g.add_dep(2, 3);
992 g.add_dep(1, 3);
993 assert_eq!(g.dependencies_of(2), vec![1]);
994 let topo = g.topological_sort();
995 assert_eq!(topo.len(), 3);
996 assert!(!g.has_cycle());
997 let pos: std::collections::HashMap<u32, usize> =
998 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
999 assert!(pos[&1] < pos[&2]);
1000 assert!(pos[&1] < pos[&3]);
1001 assert!(pos[&2] < pos[&3]);
1002 }
1003}