Skip to main content

oxilean_codegen/jvm_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::HashMap;
7
8use super::types::{
9    ConstantPool, JVMAnalysisCache, JVMConstantFoldingHelper, JVMDepGraph, JVMDominatorTree,
10    JVMExtCache, JVMExtConstFolder, JVMExtDepGraph, JVMExtDomTree, JVMExtLiveness,
11    JVMExtPassConfig, JVMExtPassPhase, JVMExtPassRegistry, JVMExtPassStats, JVMExtWorklist,
12    JVMLivenessInfo, JVMPassConfig, JVMPassPhase, JVMPassRegistry, JVMPassStats, JVMWorklist,
13    JvmBackend, JvmClass, JvmCodegenError, JvmField, JvmOpcode, JvmType, MethodDescriptor,
14};
15
16/// JVM access flag bit-masks (JVMS Table 4.5-A / 4.6-A).
17pub mod access_flags {
18    pub const PUBLIC: u16 = 0x0001;
19    pub const PRIVATE: u16 = 0x0002;
20    pub const PROTECTED: u16 = 0x0004;
21    pub const STATIC: u16 = 0x0008;
22    pub const FINAL: u16 = 0x0010;
23    pub const SUPER: u16 = 0x0020;
24    pub const SYNCHRONIZED: u16 = 0x0020;
25    pub const VOLATILE: u16 = 0x0040;
26    pub const TRANSIENT: u16 = 0x0080;
27    pub const NATIVE: u16 = 0x0100;
28    pub const INTERFACE: u16 = 0x0200;
29    pub const ABSTRACT: u16 = 0x0400;
30    pub const STRICT: u16 = 0x0800;
31    pub const SYNTHETIC: u16 = 0x1000;
32    pub const ANNOTATION: u16 = 0x2000;
33    pub const ENUM: u16 = 0x4000;
34}
35pub type JvmResult<T> = Result<T, JvmCodegenError>;
36#[cfg(test)]
37mod tests {
38    use super::*;
39    #[test]
40    pub(super) fn test_jvm_type_descriptor() {
41        assert_eq!(JvmType::Int.descriptor(), "I");
42        assert_eq!(JvmType::Long.descriptor(), "J");
43        assert_eq!(JvmType::Double.descriptor(), "D");
44        assert_eq!(JvmType::Boolean.descriptor(), "Z");
45        assert_eq!(JvmType::Void.descriptor(), "V");
46        assert_eq!(
47            JvmType::Object("java/lang/String".to_string()).descriptor(),
48            "Ljava/lang/String;"
49        );
50        assert_eq!(JvmType::Array(Box::new(JvmType::Int)).descriptor(), "[I");
51        assert_eq!(
52            JvmType::Generic("T".to_string()).descriptor(),
53            "Ljava/lang/Object;"
54        );
55    }
56    #[test]
57    pub(super) fn test_jvm_type_slot_size_and_predicates() {
58        assert_eq!(JvmType::Int.slot_size(), 1);
59        assert_eq!(JvmType::Long.slot_size(), 2);
60        assert_eq!(JvmType::Double.slot_size(), 2);
61        assert_eq!(JvmType::Object("Foo".to_string()).slot_size(), 1);
62        assert!(JvmType::Long.is_wide());
63        assert!(!JvmType::Int.is_wide());
64        assert!(JvmType::Object("Foo".to_string()).is_reference());
65        assert!(JvmType::Int.is_int_category());
66        assert!(!JvmType::Long.is_int_category());
67    }
68    #[test]
69    pub(super) fn test_method_descriptor_rendering() {
70        let md = MethodDescriptor::new(
71            vec![
72                JvmType::Int,
73                JvmType::Object("java/lang/String".to_string()),
74            ],
75            JvmType::Void,
76        );
77        assert_eq!(md.to_string(), "(ILjava/lang/String;)V");
78        let md2 = MethodDescriptor::new(vec![], JvmType::Long);
79        assert_eq!(md2.to_string(), "()J");
80    }
81    #[test]
82    pub(super) fn test_constant_pool_deduplication() {
83        let mut cp = ConstantPool::new();
84        let idx1 = cp.utf8("hello");
85        let idx2 = cp.utf8("hello");
86        let idx3 = cp.utf8("world");
87        assert_eq!(idx1, idx2);
88        assert_ne!(idx1, idx3);
89        assert_eq!(cp.entries().len(), 2);
90    }
91    #[test]
92    pub(super) fn test_jvm_class_construction_and_summary() {
93        let mut cls = JvmClass::new("com/example/Foo");
94        cls.set_superclass("com/example/Base");
95        cls.add_interface("com/example/IFoo");
96        cls.add_field(JvmField::new("count", &JvmType::Int, access_flags::PRIVATE));
97        let backend = JvmBackend::default_backend();
98        let init = backend.emit_default_init("com/example/Base");
99        cls.add_method(init);
100        assert_eq!(cls.name, "com/example/Foo");
101        assert_eq!(cls.superclass, "com/example/Base");
102        assert_eq!(cls.interfaces.len(), 1);
103        assert_eq!(cls.fields.len(), 1);
104        assert_eq!(cls.methods.len(), 1);
105        let summary = cls.summary();
106        assert!(summary.contains("com/example/Foo"));
107        assert!(summary.contains("com/example/Base"));
108        assert!(summary.contains("IFoo"));
109        assert!(summary.contains("count"));
110    }
111    #[test]
112    pub(super) fn test_emit_binop() {
113        let backend = JvmBackend::default_backend();
114        let add = backend.emit_binop("+").expect("add emit should succeed");
115        assert!(matches!(add.opcode, JvmOpcode::Iadd));
116        let mul = backend.emit_binop("mul").expect("mul emit should succeed");
117        assert!(matches!(mul.opcode, JvmOpcode::Imul));
118        let ladd = backend
119            .emit_binop("ladd")
120            .expect("ladd emit should succeed");
121        assert!(matches!(ladd.opcode, JvmOpcode::Ladd));
122        let err = backend.emit_binop("unknown");
123        assert!(err.is_err());
124    }
125    #[test]
126    pub(super) fn test_emit_load_store_return() {
127        let backend = JvmBackend::default_backend();
128        let load_int = backend.emit_load(2, &JvmType::Int);
129        assert!(matches!(load_int.opcode, JvmOpcode::Iload(2)));
130        let load_ref = backend.emit_load(0, &JvmType::Object("Foo".to_string()));
131        assert!(matches!(load_ref.opcode, JvmOpcode::Aload(0)));
132        let store_long = backend.emit_store(1, &JvmType::Long);
133        assert!(matches!(store_long.opcode, JvmOpcode::Lstore(1)));
134        let ret_void = backend.emit_return(&JvmType::Void);
135        assert!(matches!(ret_void.opcode, JvmOpcode::Return_));
136        let ret_double = backend.emit_return(&JvmType::Double);
137        assert!(matches!(ret_double.opcode, JvmOpcode::Dreturn));
138        let ret_ref = backend.emit_return(&JvmType::Object("X".to_string()));
139        assert!(matches!(ret_ref.opcode, JvmOpcode::Areturn));
140    }
141    #[test]
142    pub(super) fn test_emit_new_default_sequence() {
143        let backend = JvmBackend::default_backend();
144        let instrs = backend.emit_new_default("com/example/Foo");
145        assert_eq!(instrs.len(), 3);
146        assert!(matches!(instrs[0].opcode, JvmOpcode::New(_)));
147        assert!(matches!(instrs[1].opcode, JvmOpcode::Dup));
148        assert!(matches!(instrs[2].opcode, JvmOpcode::Invokespecial { .. }));
149    }
150    #[test]
151    pub(super) fn test_emit_clinit() {
152        let backend = JvmBackend::default_backend();
153        let clinit = backend.emit_clinit("com/example/Singleton");
154        assert_eq!(clinit.name, "<clinit>");
155        assert_eq!(clinit.descriptor, "()V");
156        assert!(clinit.access_flags & access_flags::STATIC != 0);
157        assert!(!clinit.code.is_empty());
158        assert!(matches!(
159            clinit
160                .code
161                .last()
162                .expect("opcode should be accessible")
163                .opcode,
164            JvmOpcode::Return_
165        ));
166    }
167    #[test]
168    pub(super) fn test_emit_fun_decl() {
169        let mut backend = JvmBackend::default_backend();
170        let decl = LcnfFunDecl {
171            name: "Main_hello".to_string(),
172            original_name: None,
173            params: vec![],
174            ret_type: LcnfType::Nat,
175            body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(42))),
176            is_recursive: false,
177            is_lifted: false,
178            inline_cost: 1,
179        };
180        let cls = backend
181            .emit_fun_decl(&decl)
182            .expect("cls emit should succeed");
183        assert!(cls.name.contains("hello"));
184        assert!(!cls.methods.is_empty());
185        let apply = cls.methods.iter().find(|m| m.name == "apply");
186        assert!(apply.is_some());
187        let m = apply.expect("m should be Some/Ok");
188        assert!(m.access_flags & access_flags::STATIC != 0);
189    }
190}
191#[cfg(test)]
192mod JVM_infra_tests {
193    use super::*;
194    #[test]
195    pub(super) fn test_pass_config() {
196        let config = JVMPassConfig::new("test_pass", JVMPassPhase::Transformation);
197        assert!(config.enabled);
198        assert!(config.phase.is_modifying());
199        assert_eq!(config.phase.name(), "transformation");
200    }
201    #[test]
202    pub(super) fn test_pass_stats() {
203        let mut stats = JVMPassStats::new();
204        stats.record_run(10, 100, 3);
205        stats.record_run(20, 200, 5);
206        assert_eq!(stats.total_runs, 2);
207        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
208        assert!((stats.success_rate() - 1.0).abs() < 0.01);
209        let s = stats.format_summary();
210        assert!(s.contains("Runs: 2/2"));
211    }
212    #[test]
213    pub(super) fn test_pass_registry() {
214        let mut reg = JVMPassRegistry::new();
215        reg.register(JVMPassConfig::new("pass_a", JVMPassPhase::Analysis));
216        reg.register(JVMPassConfig::new("pass_b", JVMPassPhase::Transformation).disabled());
217        assert_eq!(reg.total_passes(), 2);
218        assert_eq!(reg.enabled_count(), 1);
219        reg.update_stats("pass_a", 5, 50, 2);
220        let stats = reg.get_stats("pass_a").expect("stats should exist");
221        assert_eq!(stats.total_changes, 5);
222    }
223    #[test]
224    pub(super) fn test_analysis_cache() {
225        let mut cache = JVMAnalysisCache::new(10);
226        cache.insert("key1".to_string(), vec![1, 2, 3]);
227        assert!(cache.get("key1").is_some());
228        assert!(cache.get("key2").is_none());
229        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
230        cache.invalidate("key1");
231        assert!(!cache.entries["key1"].valid);
232        assert_eq!(cache.size(), 1);
233    }
234    #[test]
235    pub(super) fn test_worklist() {
236        let mut wl = JVMWorklist::new();
237        assert!(wl.push(1));
238        assert!(wl.push(2));
239        assert!(!wl.push(1));
240        assert_eq!(wl.len(), 2);
241        assert_eq!(wl.pop(), Some(1));
242        assert!(!wl.contains(1));
243        assert!(wl.contains(2));
244    }
245    #[test]
246    pub(super) fn test_dominator_tree() {
247        let mut dt = JVMDominatorTree::new(5);
248        dt.set_idom(1, 0);
249        dt.set_idom(2, 0);
250        dt.set_idom(3, 1);
251        assert!(dt.dominates(0, 3));
252        assert!(dt.dominates(1, 3));
253        assert!(!dt.dominates(2, 3));
254        assert!(dt.dominates(3, 3));
255    }
256    #[test]
257    pub(super) fn test_liveness() {
258        let mut liveness = JVMLivenessInfo::new(3);
259        liveness.add_def(0, 1);
260        liveness.add_use(1, 1);
261        assert!(liveness.defs[0].contains(&1));
262        assert!(liveness.uses[1].contains(&1));
263    }
264    #[test]
265    pub(super) fn test_constant_folding() {
266        assert_eq!(JVMConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
267        assert_eq!(JVMConstantFoldingHelper::fold_div_i64(10, 0), None);
268        assert_eq!(JVMConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
269        assert_eq!(
270            JVMConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
271            0b1000
272        );
273        assert_eq!(JVMConstantFoldingHelper::fold_bitnot_i64(0), -1);
274    }
275    #[test]
276    pub(super) fn test_dep_graph() {
277        let mut g = JVMDepGraph::new();
278        g.add_dep(1, 2);
279        g.add_dep(2, 3);
280        g.add_dep(1, 3);
281        assert_eq!(g.dependencies_of(2), vec![1]);
282        let topo = g.topological_sort();
283        assert_eq!(topo.len(), 3);
284        assert!(!g.has_cycle());
285        let pos: std::collections::HashMap<u32, usize> =
286            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
287        assert!(pos[&1] < pos[&2]);
288        assert!(pos[&1] < pos[&3]);
289        assert!(pos[&2] < pos[&3]);
290    }
291}
292#[cfg(test)]
293mod jvmext_pass_tests {
294    use super::*;
295    #[test]
296    pub(super) fn test_jvmext_phase_order() {
297        assert_eq!(JVMExtPassPhase::Early.order(), 0);
298        assert_eq!(JVMExtPassPhase::Middle.order(), 1);
299        assert_eq!(JVMExtPassPhase::Late.order(), 2);
300        assert_eq!(JVMExtPassPhase::Finalize.order(), 3);
301        assert!(JVMExtPassPhase::Early.is_early());
302        assert!(!JVMExtPassPhase::Early.is_late());
303    }
304    #[test]
305    pub(super) fn test_jvmext_config_builder() {
306        let c = JVMExtPassConfig::new("p")
307            .with_phase(JVMExtPassPhase::Late)
308            .with_max_iter(50)
309            .with_debug(1);
310        assert_eq!(c.name, "p");
311        assert_eq!(c.max_iterations, 50);
312        assert!(c.is_debug_enabled());
313        assert!(c.enabled);
314        let c2 = c.disabled();
315        assert!(!c2.enabled);
316    }
317    #[test]
318    pub(super) fn test_jvmext_stats() {
319        let mut s = JVMExtPassStats::new();
320        s.visit();
321        s.visit();
322        s.modify();
323        s.iterate();
324        assert_eq!(s.nodes_visited, 2);
325        assert_eq!(s.nodes_modified, 1);
326        assert!(s.changed);
327        assert_eq!(s.iterations, 1);
328        let e = s.efficiency();
329        assert!((e - 0.5).abs() < 1e-9);
330    }
331    #[test]
332    pub(super) fn test_jvmext_registry() {
333        let mut r = JVMExtPassRegistry::new();
334        r.register(JVMExtPassConfig::new("a").with_phase(JVMExtPassPhase::Early));
335        r.register(JVMExtPassConfig::new("b").disabled());
336        assert_eq!(r.len(), 2);
337        assert_eq!(r.enabled_passes().len(), 1);
338        assert_eq!(r.passes_in_phase(&JVMExtPassPhase::Early).len(), 1);
339    }
340    #[test]
341    pub(super) fn test_jvmext_cache() {
342        let mut c = JVMExtCache::new(4);
343        assert!(c.get(99).is_none());
344        c.put(99, vec![1, 2, 3]);
345        let v = c.get(99).expect("v should be present in map");
346        assert_eq!(v, &[1u8, 2, 3]);
347        assert!(c.hit_rate() > 0.0);
348        assert_eq!(c.live_count(), 1);
349    }
350    #[test]
351    pub(super) fn test_jvmext_worklist() {
352        let mut w = JVMExtWorklist::new(10);
353        w.push(5);
354        w.push(3);
355        w.push(5);
356        assert_eq!(w.len(), 2);
357        assert!(w.contains(5));
358        let first = w.pop().expect("first should be available to pop");
359        assert!(!w.contains(first));
360    }
361    #[test]
362    pub(super) fn test_jvmext_dom_tree() {
363        let mut dt = JVMExtDomTree::new(5);
364        dt.set_idom(1, 0);
365        dt.set_idom(2, 0);
366        dt.set_idom(3, 1);
367        dt.set_idom(4, 1);
368        assert!(dt.dominates(0, 3));
369        assert!(dt.dominates(1, 4));
370        assert!(!dt.dominates(2, 3));
371        assert_eq!(dt.depth_of(3), 2);
372    }
373    #[test]
374    pub(super) fn test_jvmext_liveness() {
375        let mut lv = JVMExtLiveness::new(3);
376        lv.add_def(0, 1);
377        lv.add_use(1, 1);
378        assert!(lv.var_is_def_in_block(0, 1));
379        assert!(lv.var_is_used_in_block(1, 1));
380        assert!(!lv.var_is_def_in_block(1, 1));
381    }
382    #[test]
383    pub(super) fn test_jvmext_const_folder() {
384        let mut cf = JVMExtConstFolder::new();
385        assert_eq!(cf.add_i64(3, 4), Some(7));
386        assert_eq!(cf.div_i64(10, 0), None);
387        assert_eq!(cf.mul_i64(6, 7), Some(42));
388        assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
389        assert_eq!(cf.fold_count(), 3);
390        assert_eq!(cf.failure_count(), 1);
391    }
392    #[test]
393    pub(super) fn test_jvmext_dep_graph() {
394        let mut g = JVMExtDepGraph::new(4);
395        g.add_edge(0, 1);
396        g.add_edge(1, 2);
397        g.add_edge(2, 3);
398        assert!(!g.has_cycle());
399        assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
400        assert_eq!(g.reachable(0).len(), 4);
401        let sccs = g.scc();
402        assert_eq!(sccs.len(), 4);
403    }
404}