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::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(&params[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}
480/// Set of Java reserved keywords.
481pub 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}
577/// Minimal Java runtime class emitted at the top of every generated module.
578pub 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}