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