Skip to main content

oxilean_codegen/java_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use 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
15/// Convert a primitive JavaType to its boxed reference type for use in generics.
16pub(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}
29/// Map an LCNF type to a Java type.
30pub(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(&params[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}
481/// Set of Java reserved keywords.
482pub 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}
578/// Minimal Java runtime class emitted at the top of every generated module.
579pub 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}