Skip to main content

oxilean_codegen/jvm_backend/
types.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::functions::access_flags;
9use super::functions::JvmResult;
10
11use std::collections::{HashSet, VecDeque};
12
13#[allow(dead_code)]
14#[derive(Debug, Clone)]
15pub struct JVMLivenessInfo {
16    pub live_in: Vec<std::collections::HashSet<u32>>,
17    pub live_out: Vec<std::collections::HashSet<u32>>,
18    pub defs: Vec<std::collections::HashSet<u32>>,
19    pub uses: Vec<std::collections::HashSet<u32>>,
20}
21impl JVMLivenessInfo {
22    #[allow(dead_code)]
23    pub fn new(block_count: usize) -> Self {
24        JVMLivenessInfo {
25            live_in: vec![std::collections::HashSet::new(); block_count],
26            live_out: vec![std::collections::HashSet::new(); block_count],
27            defs: vec![std::collections::HashSet::new(); block_count],
28            uses: vec![std::collections::HashSet::new(); block_count],
29        }
30    }
31    #[allow(dead_code)]
32    pub fn add_def(&mut self, block: usize, var: u32) {
33        if block < self.defs.len() {
34            self.defs[block].insert(var);
35        }
36    }
37    #[allow(dead_code)]
38    pub fn add_use(&mut self, block: usize, var: u32) {
39        if block < self.uses.len() {
40            self.uses[block].insert(var);
41        }
42    }
43    #[allow(dead_code)]
44    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
45        self.live_in
46            .get(block)
47            .map(|s| s.contains(&var))
48            .unwrap_or(false)
49    }
50    #[allow(dead_code)]
51    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
52        self.live_out
53            .get(block)
54            .map(|s| s.contains(&var))
55            .unwrap_or(false)
56    }
57}
58/// Analysis cache for JVMExt.
59#[allow(dead_code)]
60#[derive(Debug)]
61pub struct JVMExtCache {
62    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
63    pub(super) cap: usize,
64    pub(super) total_hits: u64,
65    pub(super) total_misses: u64,
66}
67impl JVMExtCache {
68    #[allow(dead_code)]
69    pub fn new(cap: usize) -> Self {
70        Self {
71            entries: Vec::new(),
72            cap,
73            total_hits: 0,
74            total_misses: 0,
75        }
76    }
77    #[allow(dead_code)]
78    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
79        for e in self.entries.iter_mut() {
80            if e.0 == key && e.2 {
81                e.3 += 1;
82                self.total_hits += 1;
83                return Some(&e.1);
84            }
85        }
86        self.total_misses += 1;
87        None
88    }
89    #[allow(dead_code)]
90    pub fn put(&mut self, key: u64, data: Vec<u8>) {
91        if self.entries.len() >= self.cap {
92            self.entries.retain(|e| e.2);
93            if self.entries.len() >= self.cap {
94                self.entries.remove(0);
95            }
96        }
97        self.entries.push((key, data, true, 0));
98    }
99    #[allow(dead_code)]
100    pub fn invalidate(&mut self) {
101        for e in self.entries.iter_mut() {
102            e.2 = false;
103        }
104    }
105    #[allow(dead_code)]
106    pub fn hit_rate(&self) -> f64 {
107        let t = self.total_hits + self.total_misses;
108        if t == 0 {
109            0.0
110        } else {
111            self.total_hits as f64 / t as f64
112        }
113    }
114    #[allow(dead_code)]
115    pub fn live_count(&self) -> usize {
116        self.entries.iter().filter(|e| e.2).count()
117    }
118}
119/// Constant folding helper for JVMExt.
120#[allow(dead_code)]
121#[derive(Debug, Clone, Default)]
122pub struct JVMExtConstFolder {
123    pub(super) folds: usize,
124    pub(super) failures: usize,
125    pub(super) enabled: bool,
126}
127impl JVMExtConstFolder {
128    #[allow(dead_code)]
129    pub fn new() -> Self {
130        Self {
131            folds: 0,
132            failures: 0,
133            enabled: true,
134        }
135    }
136    #[allow(dead_code)]
137    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
138        self.folds += 1;
139        a.checked_add(b)
140    }
141    #[allow(dead_code)]
142    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
143        self.folds += 1;
144        a.checked_sub(b)
145    }
146    #[allow(dead_code)]
147    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
148        self.folds += 1;
149        a.checked_mul(b)
150    }
151    #[allow(dead_code)]
152    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
153        if b == 0 {
154            self.failures += 1;
155            None
156        } else {
157            self.folds += 1;
158            a.checked_div(b)
159        }
160    }
161    #[allow(dead_code)]
162    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
163        if b == 0 {
164            self.failures += 1;
165            None
166        } else {
167            self.folds += 1;
168            a.checked_rem(b)
169        }
170    }
171    #[allow(dead_code)]
172    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
173        self.folds += 1;
174        a.checked_neg()
175    }
176    #[allow(dead_code)]
177    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
178        if s >= 64 {
179            self.failures += 1;
180            None
181        } else {
182            self.folds += 1;
183            a.checked_shl(s)
184        }
185    }
186    #[allow(dead_code)]
187    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
188        if s >= 64 {
189            self.failures += 1;
190            None
191        } else {
192            self.folds += 1;
193            a.checked_shr(s)
194        }
195    }
196    #[allow(dead_code)]
197    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
198        self.folds += 1;
199        a & b
200    }
201    #[allow(dead_code)]
202    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
203        self.folds += 1;
204        a | b
205    }
206    #[allow(dead_code)]
207    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
208        self.folds += 1;
209        a ^ b
210    }
211    #[allow(dead_code)]
212    pub fn not_i64(&mut self, a: i64) -> i64 {
213        self.folds += 1;
214        !a
215    }
216    #[allow(dead_code)]
217    pub fn fold_count(&self) -> usize {
218        self.folds
219    }
220    #[allow(dead_code)]
221    pub fn failure_count(&self) -> usize {
222        self.failures
223    }
224    #[allow(dead_code)]
225    pub fn enable(&mut self) {
226        self.enabled = true;
227    }
228    #[allow(dead_code)]
229    pub fn disable(&mut self) {
230        self.enabled = false;
231    }
232    #[allow(dead_code)]
233    pub fn is_enabled(&self) -> bool {
234        self.enabled
235    }
236}
237/// Code-generation errors for the JVM backend.
238#[derive(Debug, Clone)]
239pub enum JvmCodegenError {
240    /// An unsupported LCNF construct was encountered.
241    Unsupported(String),
242    /// A name lookup failed.
243    UnknownVar(String),
244    /// Internal invariant violation.
245    Internal(String),
246}
247/// The JVM backend that compiles LCNF to a `JvmClass` IR.
248pub struct JvmBackend {
249    pub(super) config: JvmConfig,
250    /// Counter for generating unique label names.
251    pub(super) label_counter: u32,
252    /// Mapping from LCNF variable names to local-variable slot numbers.
253    pub(super) locals: HashMap<String, u16>,
254    /// Next available local-variable slot.
255    pub(super) next_local: u16,
256}
257impl JvmBackend {
258    /// Create a new backend with the given configuration.
259    pub fn new(config: JvmConfig) -> Self {
260        JvmBackend {
261            config,
262            label_counter: 0,
263            locals: HashMap::new(),
264            next_local: 0,
265        }
266    }
267    /// Create a backend with the default configuration.
268    pub fn default_backend() -> Self {
269        JvmBackend::new(JvmConfig::default())
270    }
271    pub(super) fn fresh_label(&mut self, prefix: &str) -> String {
272        let n = self.label_counter;
273        self.label_counter += 1;
274        format!("{}_{}", prefix, n)
275    }
276    /// Allocate a local-variable slot for `name` with the given type.
277    pub(super) fn alloc_local(&mut self, name: &str, ty: &JvmType) -> u16 {
278        let slot = self.next_local;
279        self.locals.insert(name.to_string(), slot);
280        self.next_local += ty.slot_size() as u16;
281        slot
282    }
283    /// Look up the slot for `name`, allocating one (as Object) if absent.
284    pub(super) fn get_or_alloc_local(&mut self, name: &str) -> u16 {
285        if let Some(&slot) = self.locals.get(name) {
286            return slot;
287        }
288        self.alloc_local(name, &JvmType::Object("java/lang/Object".to_string()))
289    }
290    /// Emit instructions that push a literal value onto the operand stack.
291    pub fn emit_literal(&self, lit: &LcnfLit) -> JvmResult<Vec<JvmInstruction>> {
292        let instrs = match lit {
293            LcnfLit::Nat(n) => {
294                let v = *n as i64;
295                if v == 0 {
296                    vec![JvmInstruction::new(JvmOpcode::Lconst(0))]
297                } else if v == 1 {
298                    vec![JvmInstruction::new(JvmOpcode::Lconst(1))]
299                } else {
300                    vec![JvmInstruction::new(JvmOpcode::Ldc(0))]
301                }
302            }
303            LcnfLit::Str(_) => vec![JvmInstruction::new(JvmOpcode::Ldc(0))],
304        };
305        Ok(instrs)
306    }
307    /// Emit a simple binary arithmetic instruction for `int` operands.
308    pub fn emit_binop(&self, op: &str) -> JvmResult<JvmInstruction> {
309        let opcode = match op {
310            "add" | "+" => JvmOpcode::Iadd,
311            "sub" | "-" => JvmOpcode::Isub,
312            "mul" | "*" => JvmOpcode::Imul,
313            "div" | "/" => JvmOpcode::Idiv,
314            "rem" | "%" => JvmOpcode::Irem,
315            "ladd" => JvmOpcode::Ladd,
316            "lsub" => JvmOpcode::Lsub,
317            "lmul" => JvmOpcode::Lmul,
318            "ldiv" => JvmOpcode::Ldiv,
319            "dadd" => JvmOpcode::Dadd,
320            "dsub" => JvmOpcode::Dsub,
321            "dmul" => JvmOpcode::Dmul,
322            "ddiv" => JvmOpcode::Ddiv,
323            _ => return Err(JvmCodegenError::Unsupported(format!("binary op `{}`", op))),
324        };
325        Ok(JvmInstruction::new(opcode))
326    }
327    /// Build an `invokevirtual` instruction.
328    pub fn emit_invokevirtual(&self, class: &str, name: &str, descriptor: &str) -> JvmInstruction {
329        JvmInstruction::new(JvmOpcode::Invokevirtual {
330            class: class.to_string(),
331            name: name.to_string(),
332            descriptor: descriptor.to_string(),
333        })
334    }
335    /// Build an `invokestatic` instruction.
336    pub fn emit_invokestatic(&self, class: &str, name: &str, descriptor: &str) -> JvmInstruction {
337        JvmInstruction::new(JvmOpcode::Invokestatic {
338            class: class.to_string(),
339            name: name.to_string(),
340            descriptor: descriptor.to_string(),
341        })
342    }
343    /// Emit a `new` + `dup` + `invokespecial <init>` sequence (default ctor).
344    pub fn emit_new_default(&self, class: &str) -> Vec<JvmInstruction> {
345        vec![
346            JvmInstruction::new(JvmOpcode::New(class.to_string())),
347            JvmInstruction::new(JvmOpcode::Dup),
348            JvmInstruction::new(JvmOpcode::Invokespecial {
349                class: class.to_string(),
350                name: "<init>".to_string(),
351                descriptor: "()V".to_string(),
352            }),
353        ]
354    }
355    /// Emit a typed load instruction for the given local-variable slot.
356    pub fn emit_load(&self, slot: u16, ty: &JvmType) -> JvmInstruction {
357        let opcode = match ty {
358            JvmType::Int | JvmType::Boolean | JvmType::Byte | JvmType::Short | JvmType::Char => {
359                JvmOpcode::Iload(slot)
360            }
361            JvmType::Long => JvmOpcode::Lload(slot),
362            JvmType::Float => JvmOpcode::Fload(slot),
363            JvmType::Double => JvmOpcode::Dload(slot),
364            _ => JvmOpcode::Aload(slot),
365        };
366        JvmInstruction::new(opcode)
367    }
368    /// Emit a typed store instruction for the given local-variable slot.
369    pub fn emit_store(&self, slot: u16, ty: &JvmType) -> JvmInstruction {
370        let opcode = match ty {
371            JvmType::Int | JvmType::Boolean | JvmType::Byte | JvmType::Short | JvmType::Char => {
372                JvmOpcode::Istore(slot)
373            }
374            JvmType::Long => JvmOpcode::Lstore(slot),
375            JvmType::Float => JvmOpcode::Fstore(slot),
376            JvmType::Double => JvmOpcode::Dstore(slot),
377            _ => JvmOpcode::Astore(slot),
378        };
379        JvmInstruction::new(opcode)
380    }
381    /// Emit a typed return instruction.
382    pub fn emit_return(&self, ty: &JvmType) -> JvmInstruction {
383        let opcode = match ty {
384            JvmType::Void => JvmOpcode::Return_,
385            JvmType::Int | JvmType::Boolean | JvmType::Byte | JvmType::Short | JvmType::Char => {
386                JvmOpcode::Ireturn
387            }
388            JvmType::Long => JvmOpcode::Lreturn,
389            JvmType::Float => JvmOpcode::Freturn,
390            JvmType::Double => JvmOpcode::Dreturn,
391            _ => JvmOpcode::Areturn,
392        };
393        JvmInstruction::new(opcode)
394    }
395    /// Generate a canonical `<clinit>` that writes a fresh `INSTANCE` field.
396    pub fn emit_clinit(&self, class_name: &str) -> JvmMethod {
397        let mut code = Vec::new();
398        code.extend(self.emit_new_default(class_name));
399        code.push(JvmInstruction::new(JvmOpcode::Putstatic {
400            class: class_name.to_string(),
401            name: "INSTANCE".to_string(),
402            descriptor: format!("L{};", class_name),
403        }));
404        code.push(JvmInstruction::new(JvmOpcode::Return_));
405        JvmMethod::new("<clinit>", "()V", access_flags::STATIC, code, 3, 0)
406    }
407    /// Generate a default `<init>` constructor that delegates to `super()`.
408    pub fn emit_default_init(&self, superclass: &str) -> JvmMethod {
409        let code = vec![
410            JvmInstruction::new(JvmOpcode::Aload(0)),
411            JvmInstruction::new(JvmOpcode::Invokespecial {
412                class: superclass.to_string(),
413                name: "<init>".to_string(),
414                descriptor: "()V".to_string(),
415            }),
416            JvmInstruction::new(JvmOpcode::Return_),
417        ];
418        JvmMethod::new("<init>", "()V", access_flags::PUBLIC, code, 1, 1)
419    }
420    /// Compile an LCNF function declaration into a `JvmClass`.
421    pub fn emit_fun_decl(&mut self, decl: &LcnfFunDecl) -> JvmResult<JvmClass> {
422        let class_name = self.mangle_name(&decl.name);
423        let mut cls = JvmClass::new(&class_name);
424        cls.major_version = self.config.class_version;
425        self.locals.clear();
426        self.next_local = 0;
427        for param in &decl.params {
428            self.alloc_local(
429                &param.name,
430                &JvmType::Object("java/lang/Object".to_string()),
431            );
432        }
433        let mut code = Vec::new();
434        self.emit_lcnf_expr(&decl.body, &mut code)?;
435        code.push(JvmInstruction::new(JvmOpcode::Areturn));
436        let max_locals = self.next_local.max(1);
437        let method = JvmMethod::new(
438            "apply",
439            "()Ljava/lang/Object;",
440            access_flags::PUBLIC | access_flags::STATIC,
441            code,
442            8,
443            max_locals,
444        );
445        cls.add_method(method);
446        cls.add_method(self.emit_default_init("java/lang/Object"));
447        Ok(cls)
448    }
449    /// Recursively emit JVM instructions for an LCNF expression.
450    pub(super) fn emit_lcnf_expr(
451        &mut self,
452        expr: &LcnfExpr,
453        out: &mut Vec<JvmInstruction>,
454    ) -> JvmResult<()> {
455        match expr {
456            LcnfExpr::Let {
457                id: _,
458                ty: _,
459                name,
460                value,
461                body,
462            } => {
463                self.emit_let_value(value, out)?;
464                let slot = self.alloc_local(name, &JvmType::Object("java/lang/Object".to_string()));
465                out.push(JvmInstruction::new(JvmOpcode::Astore(slot)));
466                self.emit_lcnf_expr(body, out)?;
467            }
468            LcnfExpr::Return(arg) => {
469                self.emit_lcnf_arg(arg, out);
470            }
471            LcnfExpr::Case {
472                scrutinee,
473                scrutinee_ty: _,
474                alts,
475                default,
476            } => {
477                let scrut_slot = self.get_or_alloc_local(&format!("_x{}", scrutinee.0));
478                out.push(JvmInstruction::new(JvmOpcode::Aload(scrut_slot)));
479                let end_label = self.fresh_label("case_end");
480                for alt in alts {
481                    let skip_label = self.fresh_label("alt_skip");
482                    out.push(JvmInstruction::new(JvmOpcode::Dup));
483                    out.push(JvmInstruction::new(JvmOpcode::Instanceof(
484                        alt.ctor_name.clone(),
485                    )));
486                    out.push(JvmInstruction::new(JvmOpcode::Ifeq(0)));
487                    let branch_idx = out.len() - 1;
488                    for (i, param) in alt.params.iter().enumerate() {
489                        out.push(JvmInstruction::new(JvmOpcode::Dup));
490                        out.push(JvmInstruction::new(JvmOpcode::Getfield {
491                            class: alt.ctor_name.clone(),
492                            name: format!("field{}", i),
493                            descriptor: "Ljava/lang/Object;".to_string(),
494                        }));
495                        let slot = self.alloc_local(
496                            &param.name,
497                            &JvmType::Object("java/lang/Object".to_string()),
498                        );
499                        out.push(JvmInstruction::new(JvmOpcode::Astore(slot)));
500                    }
501                    out.push(JvmInstruction::new(JvmOpcode::Pop));
502                    self.emit_lcnf_expr(&alt.body, out)?;
503                    out.push(JvmInstruction::new(JvmOpcode::Goto(0)));
504                    let goto_end_idx = out.len() - 1;
505                    out.push(JvmInstruction::new(JvmOpcode::Label(skip_label)));
506                    let skip_offset = (out.len() - branch_idx) as i16;
507                    if let JvmOpcode::Ifeq(ref mut off) = out[branch_idx].opcode {
508                        *off = skip_offset;
509                    }
510                    let end_offset = (out.len() - goto_end_idx) as i16;
511                    if let JvmOpcode::Goto(ref mut off) = out[goto_end_idx].opcode {
512                        *off = end_offset;
513                    }
514                }
515                out.push(JvmInstruction::new(JvmOpcode::Pop));
516                if let Some(def) = default {
517                    self.emit_lcnf_expr(def, out)?;
518                } else {
519                    out.push(JvmInstruction::new(JvmOpcode::AconstNull));
520                    out.push(JvmInstruction::new(JvmOpcode::Athrow));
521                }
522                out.push(JvmInstruction::new(JvmOpcode::Label(end_label)));
523            }
524            LcnfExpr::Unreachable => {
525                out.push(JvmInstruction::new(JvmOpcode::AconstNull));
526                out.push(JvmInstruction::new(JvmOpcode::Athrow));
527            }
528            LcnfExpr::TailCall(func, args) => {
529                self.emit_lcnf_arg(func, out);
530                for arg in args {
531                    self.emit_lcnf_arg(arg, out);
532                }
533                out.push(self.emit_invokevirtual(
534                    "oxilean/runtime/Closure",
535                    "apply",
536                    "(Ljava/lang/Object;)Ljava/lang/Object;",
537                ));
538            }
539        }
540        Ok(())
541    }
542    /// Emit instructions for a `LcnfLetValue`.
543    pub(super) fn emit_let_value(
544        &mut self,
545        val: &LcnfLetValue,
546        out: &mut Vec<JvmInstruction>,
547    ) -> JvmResult<()> {
548        match val {
549            LcnfLetValue::Lit(lit) => {
550                let instrs = self.emit_literal(lit)?;
551                out.extend(instrs);
552            }
553            LcnfLetValue::FVar(id) => {
554                let slot = self.get_or_alloc_local(&format!("_x{}", id.0));
555                out.push(JvmInstruction::new(JvmOpcode::Aload(slot)));
556            }
557            LcnfLetValue::App(func, args) => {
558                self.emit_lcnf_arg(func, out);
559                for arg in args {
560                    self.emit_lcnf_arg(arg, out);
561                }
562                out.push(self.emit_invokevirtual(
563                    "oxilean/runtime/Closure",
564                    "apply",
565                    "(Ljava/lang/Object;)Ljava/lang/Object;",
566                ));
567            }
568            LcnfLetValue::Ctor(name, _tag, args) => {
569                let cls = self.mangle_name(name);
570                out.extend(self.emit_new_default(&cls.clone()));
571                for (i, arg) in args.iter().enumerate() {
572                    out.push(JvmInstruction::new(JvmOpcode::Dup));
573                    self.emit_lcnf_arg(arg, out);
574                    out.push(JvmInstruction::new(JvmOpcode::Putfield {
575                        class: cls.clone(),
576                        name: format!("field{}", i),
577                        descriptor: "Ljava/lang/Object;".to_string(),
578                    }));
579                }
580            }
581            LcnfLetValue::Proj(_field_name, idx, var) => {
582                let slot = self.get_or_alloc_local(&format!("_x{}", var.0));
583                out.push(JvmInstruction::new(JvmOpcode::Aload(slot)));
584                out.push(JvmInstruction::new(JvmOpcode::Getfield {
585                    class: "oxilean/runtime/Record".to_string(),
586                    name: format!("field{}", idx),
587                    descriptor: "Ljava/lang/Object;".to_string(),
588                }));
589            }
590            LcnfLetValue::Erased => {
591                out.push(JvmInstruction::new(JvmOpcode::AconstNull));
592            }
593            LcnfLetValue::Reset(var) => {
594                let slot = self.get_or_alloc_local(&format!("_x{}", var.0));
595                out.push(JvmInstruction::new(JvmOpcode::Aload(slot)));
596            }
597            LcnfLetValue::Reuse(_slot_var, name, _tag, args) => {
598                let cls = self.mangle_name(name);
599                out.extend(self.emit_new_default(&cls.clone()));
600                for (i, arg) in args.iter().enumerate() {
601                    out.push(JvmInstruction::new(JvmOpcode::Dup));
602                    self.emit_lcnf_arg(arg, out);
603                    out.push(JvmInstruction::new(JvmOpcode::Putfield {
604                        class: cls.clone(),
605                        name: format!("field{}", i),
606                        descriptor: "Ljava/lang/Object;".to_string(),
607                    }));
608                }
609            }
610        }
611        Ok(())
612    }
613    /// Emit instructions that push an `LcnfArg` onto the operand stack.
614    pub(super) fn emit_lcnf_arg(&mut self, arg: &LcnfArg, out: &mut Vec<JvmInstruction>) {
615        match arg {
616            LcnfArg::Var(id) => {
617                let slot = self.get_or_alloc_local(&format!("_x{}", id.0));
618                out.push(JvmInstruction::new(JvmOpcode::Aload(slot)));
619            }
620            LcnfArg::Lit(lit) => {
621                if let Ok(instrs) = self.emit_literal(lit) {
622                    out.extend(instrs);
623                } else {
624                    out.push(JvmInstruction::new(JvmOpcode::AconstNull));
625                }
626            }
627            LcnfArg::Erased | LcnfArg::Type(_) => {
628                out.push(JvmInstruction::new(JvmOpcode::AconstNull));
629            }
630        }
631    }
632    /// Convert an OxiLean qualified name to a JVM binary class name.
633    pub(super) fn mangle_name(&self, name: &str) -> String {
634        let pkg = self.config.package.replace('.', "/");
635        let cls = name.replace('.', "_").replace("::", "_");
636        format!("{}/{}", pkg, cls)
637    }
638}
639/// A single JVM instruction together with optional debug metadata.
640#[derive(Debug, Clone)]
641pub struct JvmInstruction {
642    /// The actual opcode (and its inline operands).
643    pub opcode: JvmOpcode,
644    /// Optional source-line number for debugging.
645    pub line: Option<u32>,
646}
647impl JvmInstruction {
648    /// Create an instruction without line-number information.
649    pub fn new(opcode: JvmOpcode) -> Self {
650        JvmInstruction { opcode, line: None }
651    }
652    /// Create an instruction with a source-line number.
653    pub fn with_line(opcode: JvmOpcode, line: u32) -> Self {
654        JvmInstruction {
655            opcode,
656            line: Some(line),
657        }
658    }
659}
660/// A representative subset of JVM bytecode opcodes.
661///
662/// Names follow the JVM specification closely, with `_` appended where the
663/// mnemonic is a Rust keyword (e.g. `Return_`).
664#[derive(Debug, Clone, PartialEq)]
665pub enum JvmOpcode {
666    /// Push `null` reference onto the operand stack.
667    AconstNull,
668    /// Push `int` constant −1 through 5 (`iconst_<i>`).
669    Iconst(i32),
670    /// Push `long` constant 0 or 1 (`lconst_<l>`).
671    Lconst(i64),
672    /// Push `float` constant 0.0, 1.0, or 2.0 (`fconst_<f>`).
673    Fconst(f32),
674    /// Push `double` constant 0.0 or 1.0 (`dconst_<d>`).
675    Dconst(f64),
676    /// Push `byte` immediate as `int` (`bipush`).
677    Bipush(i8),
678    /// Push `short` immediate as `int` (`sipush`).
679    Sipush(i16),
680    /// Load constant from constant pool (`ldc` / `ldc_w` / `ldc2_w`).
681    Ldc(u16),
682    /// Load `int` from local variable `n` (`iload`).
683    Iload(u16),
684    /// Load `long` from local variable `n` (`lload`).
685    Lload(u16),
686    /// Load `float` from local variable `n` (`fload`).
687    Fload(u16),
688    /// Load `double` from local variable `n` (`dload`).
689    Dload(u16),
690    /// Load reference from local variable `n` (`aload`).
691    Aload(u16),
692    /// Store `int` to local variable `n` (`istore`).
693    Istore(u16),
694    /// Store `long` to local variable `n` (`lstore`).
695    Lstore(u16),
696    /// Store `float` to local variable `n` (`fstore`).
697    Fstore(u16),
698    /// Store `double` to local variable `n` (`dstore`).
699    Dstore(u16),
700    /// Store reference to local variable `n` (`astore`).
701    Astore(u16),
702    /// Load `int` from array (`iaload`).
703    Iaload,
704    /// Load reference from array (`aaload`).
705    Aaload,
706    /// Store `int` into array (`iastore`).
707    Iastore,
708    /// Store reference into array (`aastore`).
709    Aastore,
710    /// Discard top value (`pop`).
711    Pop,
712    /// Discard top one or two values (`pop2`).
713    Pop2,
714    /// Duplicate the top value (`dup`).
715    Dup,
716    /// Swap the two top values (`swap`).
717    Swap,
718    /// Add two `int` values (`iadd`).
719    Iadd,
720    /// Subtract two `int` values (`isub`).
721    Isub,
722    /// Multiply two `int` values (`imul`).
723    Imul,
724    /// Divide two `int` values (`idiv`).
725    Idiv,
726    /// `int` remainder (`irem`).
727    Irem,
728    /// Negate `int` (`ineg`).
729    Ineg,
730    /// Add two `long` values (`ladd`).
731    Ladd,
732    /// Subtract two `long` values (`lsub`).
733    Lsub,
734    /// Multiply two `long` values (`lmul`).
735    Lmul,
736    /// Divide two `long` values (`ldiv`).
737    Ldiv,
738    /// Add two `double` values (`dadd`).
739    Dadd,
740    /// Subtract two `double` values (`dsub`).
741    Dsub,
742    /// Multiply two `double` values (`dmul`).
743    Dmul,
744    /// Divide two `double` values (`ddiv`).
745    Ddiv,
746    /// Convert `int` to `long` (`i2l`).
747    I2l,
748    /// Convert `int` to `double` (`i2d`).
749    I2d,
750    /// Convert `long` to `int` (`l2i`).
751    L2i,
752    /// Convert `double` to `int` (`d2i`).
753    D2i,
754    /// Compare two `int` values and branch if equal (`if_icmpeq`).
755    IfIcmpeq(i16),
756    /// Compare two `int` values and branch if not equal (`if_icmpne`).
757    IfIcmpne(i16),
758    /// Compare two `int` values and branch if less than (`if_icmplt`).
759    IfIcmplt(i16),
760    /// Compare two `int` values and branch if greater than or equal (`if_icmpge`).
761    IfIcmpge(i16),
762    /// Compare two `int` values and branch if greater than (`if_icmpgt`).
763    IfIcmpgt(i16),
764    /// Compare two `int` values and branch if less than or equal (`if_icmple`).
765    IfIcmple(i16),
766    /// Branch if reference is `null` (`ifnull`).
767    Ifnull(i16),
768    /// Branch if reference is not `null` (`ifnonnull`).
769    Ifnonnull(i16),
770    /// Branch if `int` is zero (`ifeq`).
771    Ifeq(i16),
772    /// Branch if `int` is non-zero (`ifne`).
773    Ifne(i16),
774    /// Compare two `long` values; push −1, 0, or 1 (`lcmp`).
775    Lcmp,
776    /// Unconditional branch (`goto`).
777    Goto(i16),
778    /// Return `void` from method (`return`).
779    Return_,
780    /// Return `int` from method (`ireturn`).
781    Ireturn,
782    /// Return `long` from method (`lreturn`).
783    Lreturn,
784    /// Return `float` from method (`freturn`).
785    Freturn,
786    /// Return `double` from method (`dreturn`).
787    Dreturn,
788    /// Return reference from method (`areturn`).
789    Areturn,
790    /// Throw an exception (`athrow`).
791    Athrow,
792    /// Get instance field value (`getfield`).
793    Getfield {
794        class: String,
795        name: String,
796        descriptor: String,
797    },
798    /// Set instance field value (`putfield`).
799    Putfield {
800        class: String,
801        name: String,
802        descriptor: String,
803    },
804    /// Get static field value (`getstatic`).
805    Getstatic {
806        class: String,
807        name: String,
808        descriptor: String,
809    },
810    /// Set static field value (`putstatic`).
811    Putstatic {
812        class: String,
813        name: String,
814        descriptor: String,
815    },
816    /// Invoke instance method (`invokevirtual`).
817    Invokevirtual {
818        class: String,
819        name: String,
820        descriptor: String,
821    },
822    /// Invoke interface method (`invokeinterface`).
823    Invokeinterface {
824        class: String,
825        name: String,
826        descriptor: String,
827        count: u8,
828    },
829    /// Invoke a special (constructor / private / super) method (`invokespecial`).
830    Invokespecial {
831        class: String,
832        name: String,
833        descriptor: String,
834    },
835    /// Invoke a static method (`invokestatic`).
836    Invokestatic {
837        class: String,
838        name: String,
839        descriptor: String,
840    },
841    /// Create new object (`new`).
842    New(String),
843    /// Create new array of primitive type (`newarray`).
844    Newarray(JvmType),
845    /// Create new array of reference type (`anewarray`).
846    Anewarray(String),
847    /// Get array length (`arraylength`).
848    Arraylength,
849    /// Check whether object is instance of class (`instanceof`).
850    Instanceof(String),
851    /// Cast object to class, throwing if incompatible (`checkcast`).
852    Checkcast(String),
853    /// Label pseudo-instruction used for branch target resolution.
854    Label(String),
855    /// Increment local variable by constant (`iinc`).
856    Iinc { index: u16, constant: i16 },
857}
858#[allow(dead_code)]
859#[derive(Debug, Clone, PartialEq)]
860pub enum JVMPassPhase {
861    Analysis,
862    Transformation,
863    Verification,
864    Cleanup,
865}
866impl JVMPassPhase {
867    #[allow(dead_code)]
868    pub fn name(&self) -> &str {
869        match self {
870            JVMPassPhase::Analysis => "analysis",
871            JVMPassPhase::Transformation => "transformation",
872            JVMPassPhase::Verification => "verification",
873            JVMPassPhase::Cleanup => "cleanup",
874        }
875    }
876    #[allow(dead_code)]
877    pub fn is_modifying(&self) -> bool {
878        matches!(self, JVMPassPhase::Transformation | JVMPassPhase::Cleanup)
879    }
880}
881/// An entry in the class-file constant pool.
882#[derive(Debug, Clone, PartialEq)]
883pub enum ConstantPoolEntry {
884    /// CONSTANT_Utf8 — string data
885    Utf8(String),
886    /// CONSTANT_Integer — 32-bit integer constant
887    Integer(i32),
888    /// CONSTANT_Long — 64-bit integer constant
889    Long(i64),
890    /// CONSTANT_Float — 32-bit float constant
891    Float(f32),
892    /// CONSTANT_Double — 64-bit float constant
893    Double(f64),
894    /// CONSTANT_Class — reference to a class by Utf8 index
895    Class { name_index: u16 },
896    /// CONSTANT_String — reference to a Utf8 string value
897    StringRef { string_index: u16 },
898    /// CONSTANT_Fieldref
899    Fieldref {
900        class_index: u16,
901        name_and_type_index: u16,
902    },
903    /// CONSTANT_Methodref
904    Methodref {
905        class_index: u16,
906        name_and_type_index: u16,
907    },
908    /// CONSTANT_InterfaceMethodref
909    InterfaceMethodref {
910        class_index: u16,
911        name_and_type_index: u16,
912    },
913    /// CONSTANT_NameAndType
914    NameAndType {
915        name_index: u16,
916        descriptor_index: u16,
917    },
918}
919#[allow(dead_code)]
920#[derive(Debug, Clone)]
921pub struct JVMPassConfig {
922    pub phase: JVMPassPhase,
923    pub enabled: bool,
924    pub max_iterations: u32,
925    pub debug_output: bool,
926    pub pass_name: String,
927}
928impl JVMPassConfig {
929    #[allow(dead_code)]
930    pub fn new(name: impl Into<String>, phase: JVMPassPhase) -> Self {
931        JVMPassConfig {
932            phase,
933            enabled: true,
934            max_iterations: 10,
935            debug_output: false,
936            pass_name: name.into(),
937        }
938    }
939    #[allow(dead_code)]
940    pub fn disabled(mut self) -> Self {
941        self.enabled = false;
942        self
943    }
944    #[allow(dead_code)]
945    pub fn with_debug(mut self) -> Self {
946        self.debug_output = true;
947        self
948    }
949    #[allow(dead_code)]
950    pub fn max_iter(mut self, n: u32) -> Self {
951        self.max_iterations = n;
952        self
953    }
954}
955#[allow(dead_code)]
956pub struct JVMConstantFoldingHelper;
957impl JVMConstantFoldingHelper {
958    #[allow(dead_code)]
959    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
960        a.checked_add(b)
961    }
962    #[allow(dead_code)]
963    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
964        a.checked_sub(b)
965    }
966    #[allow(dead_code)]
967    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
968        a.checked_mul(b)
969    }
970    #[allow(dead_code)]
971    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
972        if b == 0 {
973            None
974        } else {
975            a.checked_div(b)
976        }
977    }
978    #[allow(dead_code)]
979    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
980        a + b
981    }
982    #[allow(dead_code)]
983    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
984        a * b
985    }
986    #[allow(dead_code)]
987    pub fn fold_neg_i64(a: i64) -> Option<i64> {
988        a.checked_neg()
989    }
990    #[allow(dead_code)]
991    pub fn fold_not_bool(a: bool) -> bool {
992        !a
993    }
994    #[allow(dead_code)]
995    pub fn fold_and_bool(a: bool, b: bool) -> bool {
996        a && b
997    }
998    #[allow(dead_code)]
999    pub fn fold_or_bool(a: bool, b: bool) -> bool {
1000        a || b
1001    }
1002    #[allow(dead_code)]
1003    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1004        a.checked_shl(b)
1005    }
1006    #[allow(dead_code)]
1007    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1008        a.checked_shr(b)
1009    }
1010    #[allow(dead_code)]
1011    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1012        if b == 0 {
1013            None
1014        } else {
1015            Some(a % b)
1016        }
1017    }
1018    #[allow(dead_code)]
1019    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1020        a & b
1021    }
1022    #[allow(dead_code)]
1023    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1024        a | b
1025    }
1026    #[allow(dead_code)]
1027    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1028        a ^ b
1029    }
1030    #[allow(dead_code)]
1031    pub fn fold_bitnot_i64(a: i64) -> i64 {
1032        !a
1033    }
1034}
1035/// Liveness analysis for JVMExt.
1036#[allow(dead_code)]
1037#[derive(Debug, Clone, Default)]
1038pub struct JVMExtLiveness {
1039    pub live_in: Vec<Vec<usize>>,
1040    pub live_out: Vec<Vec<usize>>,
1041    pub defs: Vec<Vec<usize>>,
1042    pub uses: Vec<Vec<usize>>,
1043}
1044impl JVMExtLiveness {
1045    #[allow(dead_code)]
1046    pub fn new(n: usize) -> Self {
1047        Self {
1048            live_in: vec![Vec::new(); n],
1049            live_out: vec![Vec::new(); n],
1050            defs: vec![Vec::new(); n],
1051            uses: vec![Vec::new(); n],
1052        }
1053    }
1054    #[allow(dead_code)]
1055    pub fn live_in(&self, b: usize, v: usize) -> bool {
1056        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1057    }
1058    #[allow(dead_code)]
1059    pub fn live_out(&self, b: usize, v: usize) -> bool {
1060        self.live_out
1061            .get(b)
1062            .map(|s| s.contains(&v))
1063            .unwrap_or(false)
1064    }
1065    #[allow(dead_code)]
1066    pub fn add_def(&mut self, b: usize, v: usize) {
1067        if let Some(s) = self.defs.get_mut(b) {
1068            if !s.contains(&v) {
1069                s.push(v);
1070            }
1071        }
1072    }
1073    #[allow(dead_code)]
1074    pub fn add_use(&mut self, b: usize, v: usize) {
1075        if let Some(s) = self.uses.get_mut(b) {
1076            if !s.contains(&v) {
1077                s.push(v);
1078            }
1079        }
1080    }
1081    #[allow(dead_code)]
1082    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1083        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1084    }
1085    #[allow(dead_code)]
1086    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1087        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1088    }
1089}
1090/// Worklist for JVMExt.
1091#[allow(dead_code)]
1092#[derive(Debug, Clone)]
1093pub struct JVMExtWorklist {
1094    pub(super) items: std::collections::VecDeque<usize>,
1095    pub(super) present: Vec<bool>,
1096}
1097impl JVMExtWorklist {
1098    #[allow(dead_code)]
1099    pub fn new(capacity: usize) -> Self {
1100        Self {
1101            items: std::collections::VecDeque::new(),
1102            present: vec![false; capacity],
1103        }
1104    }
1105    #[allow(dead_code)]
1106    pub fn push(&mut self, id: usize) {
1107        if id < self.present.len() && !self.present[id] {
1108            self.present[id] = true;
1109            self.items.push_back(id);
1110        }
1111    }
1112    #[allow(dead_code)]
1113    pub fn push_front(&mut self, id: usize) {
1114        if id < self.present.len() && !self.present[id] {
1115            self.present[id] = true;
1116            self.items.push_front(id);
1117        }
1118    }
1119    #[allow(dead_code)]
1120    pub fn pop(&mut self) -> Option<usize> {
1121        let id = self.items.pop_front()?;
1122        if id < self.present.len() {
1123            self.present[id] = false;
1124        }
1125        Some(id)
1126    }
1127    #[allow(dead_code)]
1128    pub fn is_empty(&self) -> bool {
1129        self.items.is_empty()
1130    }
1131    #[allow(dead_code)]
1132    pub fn len(&self) -> usize {
1133        self.items.len()
1134    }
1135    #[allow(dead_code)]
1136    pub fn contains(&self, id: usize) -> bool {
1137        id < self.present.len() && self.present[id]
1138    }
1139    #[allow(dead_code)]
1140    pub fn drain_all(&mut self) -> Vec<usize> {
1141        let v: Vec<usize> = self.items.drain(..).collect();
1142        for &id in &v {
1143            if id < self.present.len() {
1144                self.present[id] = false;
1145            }
1146        }
1147        v
1148    }
1149}
1150/// Complete representation of a JVM class file (IR level).
1151#[derive(Debug, Clone)]
1152pub struct JvmClass {
1153    /// Binary class name using `/` separators (e.g. `"com/example/Foo"`).
1154    pub name: String,
1155    /// Superclass binary name (`"java/lang/Object"` by default).
1156    pub superclass: String,
1157    /// Implemented interfaces (binary names).
1158    pub interfaces: Vec<String>,
1159    /// Instance and static fields.
1160    pub fields: Vec<JvmField>,
1161    /// Methods.
1162    pub methods: Vec<JvmMethod>,
1163    /// Class-level access flags.
1164    pub access_flags: u16,
1165    /// Constant pool.
1166    pub constant_pool: ConstantPool,
1167    /// Class-file major version (e.g. 65 = Java 21).
1168    pub major_version: u16,
1169    /// Source file attribute (optional).
1170    pub source_file: Option<String>,
1171}
1172impl JvmClass {
1173    pub fn new(name: &str) -> Self {
1174        JvmClass {
1175            name: name.to_string(),
1176            superclass: "java/lang/Object".to_string(),
1177            interfaces: Vec::new(),
1178            fields: Vec::new(),
1179            methods: Vec::new(),
1180            access_flags: access_flags::PUBLIC | access_flags::SUPER,
1181            constant_pool: ConstantPool::new(),
1182            major_version: 65,
1183            source_file: None,
1184        }
1185    }
1186    /// Add a field definition.
1187    pub fn add_field(&mut self, field: JvmField) {
1188        self.fields.push(field);
1189    }
1190    /// Add a method definition.
1191    pub fn add_method(&mut self, method: JvmMethod) {
1192        self.methods.push(method);
1193    }
1194    /// Add an implemented interface.
1195    pub fn add_interface(&mut self, iface: &str) {
1196        self.interfaces.push(iface.to_string());
1197    }
1198    /// Set the superclass binary name.
1199    pub fn set_superclass(&mut self, super_name: &str) {
1200        self.superclass = super_name.to_string();
1201    }
1202    /// Render a human-readable summary (not real bytecode).
1203    pub fn summary(&self) -> String {
1204        let mut out = String::new();
1205        out.push_str(&format!("class {} extends {}", self.name, self.superclass));
1206        if !self.interfaces.is_empty() {
1207            out.push_str(&format!(" implements {}", self.interfaces.join(", ")));
1208        }
1209        out.push_str(" {\n");
1210        for f in &self.fields {
1211            out.push_str(&format!("  field {} : {}\n", f.name, f.descriptor));
1212        }
1213        for m in &self.methods {
1214            out.push_str(&format!(
1215                "  method {} {} ({} instructions)\n",
1216                m.name,
1217                m.descriptor,
1218                m.code.len()
1219            ));
1220        }
1221        out.push('}');
1222        out
1223    }
1224}
1225/// Configuration for JVMExt passes.
1226#[allow(dead_code)]
1227#[derive(Debug, Clone)]
1228pub struct JVMExtPassConfig {
1229    pub name: String,
1230    pub phase: JVMExtPassPhase,
1231    pub enabled: bool,
1232    pub max_iterations: usize,
1233    pub debug: u32,
1234    pub timeout_ms: Option<u64>,
1235}
1236impl JVMExtPassConfig {
1237    #[allow(dead_code)]
1238    pub fn new(name: impl Into<String>) -> Self {
1239        Self {
1240            name: name.into(),
1241            phase: JVMExtPassPhase::Middle,
1242            enabled: true,
1243            max_iterations: 100,
1244            debug: 0,
1245            timeout_ms: None,
1246        }
1247    }
1248    #[allow(dead_code)]
1249    pub fn with_phase(mut self, phase: JVMExtPassPhase) -> Self {
1250        self.phase = phase;
1251        self
1252    }
1253    #[allow(dead_code)]
1254    pub fn with_max_iter(mut self, n: usize) -> Self {
1255        self.max_iterations = n;
1256        self
1257    }
1258    #[allow(dead_code)]
1259    pub fn with_debug(mut self, d: u32) -> Self {
1260        self.debug = d;
1261        self
1262    }
1263    #[allow(dead_code)]
1264    pub fn disabled(mut self) -> Self {
1265        self.enabled = false;
1266        self
1267    }
1268    #[allow(dead_code)]
1269    pub fn with_timeout(mut self, ms: u64) -> Self {
1270        self.timeout_ms = Some(ms);
1271        self
1272    }
1273    #[allow(dead_code)]
1274    pub fn is_debug_enabled(&self) -> bool {
1275        self.debug > 0
1276    }
1277}
1278/// Dominator tree for JVMExt.
1279#[allow(dead_code)]
1280#[derive(Debug, Clone)]
1281pub struct JVMExtDomTree {
1282    pub(super) idom: Vec<Option<usize>>,
1283    pub(super) children: Vec<Vec<usize>>,
1284    pub(super) depth: Vec<usize>,
1285}
1286impl JVMExtDomTree {
1287    #[allow(dead_code)]
1288    pub fn new(n: usize) -> Self {
1289        Self {
1290            idom: vec![None; n],
1291            children: vec![Vec::new(); n],
1292            depth: vec![0; n],
1293        }
1294    }
1295    #[allow(dead_code)]
1296    pub fn set_idom(&mut self, node: usize, dom: usize) {
1297        if node < self.idom.len() {
1298            self.idom[node] = Some(dom);
1299            if dom < self.children.len() {
1300                self.children[dom].push(node);
1301            }
1302            self.depth[node] = if dom < self.depth.len() {
1303                self.depth[dom] + 1
1304            } else {
1305                1
1306            };
1307        }
1308    }
1309    #[allow(dead_code)]
1310    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1311        if a == b {
1312            return true;
1313        }
1314        let n = self.idom.len();
1315        for _ in 0..n {
1316            match self.idom.get(b).copied().flatten() {
1317                None => return false,
1318                Some(p) if p == a => return true,
1319                Some(p) if p == b => return false,
1320                Some(p) => b = p,
1321            }
1322        }
1323        false
1324    }
1325    #[allow(dead_code)]
1326    pub fn children_of(&self, n: usize) -> &[usize] {
1327        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1328    }
1329    #[allow(dead_code)]
1330    pub fn depth_of(&self, n: usize) -> usize {
1331        self.depth.get(n).copied().unwrap_or(0)
1332    }
1333    #[allow(dead_code)]
1334    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1335        let n = self.idom.len();
1336        for _ in 0..(2 * n) {
1337            if a == b {
1338                return a;
1339            }
1340            if self.depth_of(a) > self.depth_of(b) {
1341                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1342            } else {
1343                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1344            }
1345        }
1346        0
1347    }
1348}
1349/// Pass registry for JVMExt.
1350#[allow(dead_code)]
1351#[derive(Debug, Default)]
1352pub struct JVMExtPassRegistry {
1353    pub(super) configs: Vec<JVMExtPassConfig>,
1354    pub(super) stats: Vec<JVMExtPassStats>,
1355}
1356impl JVMExtPassRegistry {
1357    #[allow(dead_code)]
1358    pub fn new() -> Self {
1359        Self::default()
1360    }
1361    #[allow(dead_code)]
1362    pub fn register(&mut self, c: JVMExtPassConfig) {
1363        self.stats.push(JVMExtPassStats::new());
1364        self.configs.push(c);
1365    }
1366    #[allow(dead_code)]
1367    pub fn len(&self) -> usize {
1368        self.configs.len()
1369    }
1370    #[allow(dead_code)]
1371    pub fn is_empty(&self) -> bool {
1372        self.configs.is_empty()
1373    }
1374    #[allow(dead_code)]
1375    pub fn get(&self, i: usize) -> Option<&JVMExtPassConfig> {
1376        self.configs.get(i)
1377    }
1378    #[allow(dead_code)]
1379    pub fn get_stats(&self, i: usize) -> Option<&JVMExtPassStats> {
1380        self.stats.get(i)
1381    }
1382    #[allow(dead_code)]
1383    pub fn enabled_passes(&self) -> Vec<&JVMExtPassConfig> {
1384        self.configs.iter().filter(|c| c.enabled).collect()
1385    }
1386    #[allow(dead_code)]
1387    pub fn passes_in_phase(&self, ph: &JVMExtPassPhase) -> Vec<&JVMExtPassConfig> {
1388        self.configs
1389            .iter()
1390            .filter(|c| c.enabled && &c.phase == ph)
1391            .collect()
1392    }
1393    #[allow(dead_code)]
1394    pub fn total_nodes_visited(&self) -> usize {
1395        self.stats.iter().map(|s| s.nodes_visited).sum()
1396    }
1397    #[allow(dead_code)]
1398    pub fn any_changed(&self) -> bool {
1399        self.stats.iter().any(|s| s.changed)
1400    }
1401}
1402/// Pass execution phase for JVMExt.
1403#[allow(dead_code)]
1404#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1405pub enum JVMExtPassPhase {
1406    Early,
1407    Middle,
1408    Late,
1409    Finalize,
1410}
1411impl JVMExtPassPhase {
1412    #[allow(dead_code)]
1413    pub fn is_early(&self) -> bool {
1414        matches!(self, Self::Early)
1415    }
1416    #[allow(dead_code)]
1417    pub fn is_middle(&self) -> bool {
1418        matches!(self, Self::Middle)
1419    }
1420    #[allow(dead_code)]
1421    pub fn is_late(&self) -> bool {
1422        matches!(self, Self::Late)
1423    }
1424    #[allow(dead_code)]
1425    pub fn is_finalize(&self) -> bool {
1426        matches!(self, Self::Finalize)
1427    }
1428    #[allow(dead_code)]
1429    pub fn order(&self) -> u32 {
1430        match self {
1431            Self::Early => 0,
1432            Self::Middle => 1,
1433            Self::Late => 2,
1434            Self::Finalize => 3,
1435        }
1436    }
1437    #[allow(dead_code)]
1438    pub fn from_order(n: u32) -> Option<Self> {
1439        match n {
1440            0 => Some(Self::Early),
1441            1 => Some(Self::Middle),
1442            2 => Some(Self::Late),
1443            3 => Some(Self::Finalize),
1444            _ => None,
1445        }
1446    }
1447}
1448/// A single JVM method (Code attribute + metadata).
1449#[derive(Debug, Clone)]
1450pub struct JvmMethod {
1451    /// Simple method name (e.g. `"<init>"`, `"apply"`).
1452    pub name: String,
1453    /// Method descriptor string (e.g. `"(I)V"`).
1454    pub descriptor: String,
1455    /// Access flags bitmask.
1456    pub access_flags: u16,
1457    /// Bytecode instructions.
1458    pub code: Vec<JvmInstruction>,
1459    /// Maximum operand-stack depth.
1460    pub max_stack: u16,
1461    /// Maximum number of local variables (including `this`).
1462    pub max_locals: u16,
1463    /// Exception table entries.
1464    pub exceptions: Vec<ExceptionEntry>,
1465}
1466impl JvmMethod {
1467    #[allow(clippy::too_many_arguments)]
1468    pub fn new(
1469        name: &str,
1470        descriptor: &str,
1471        access_flags: u16,
1472        code: Vec<JvmInstruction>,
1473        max_stack: u16,
1474        max_locals: u16,
1475    ) -> Self {
1476        JvmMethod {
1477            name: name.to_string(),
1478            descriptor: descriptor.to_string(),
1479            access_flags,
1480            code,
1481            max_stack,
1482            max_locals,
1483            exceptions: Vec::new(),
1484        }
1485    }
1486    /// Add an exception handler entry.
1487    pub fn add_exception(&mut self, entry: ExceptionEntry) {
1488        self.exceptions.push(entry);
1489    }
1490    /// Is this method abstract (no Code attribute)?
1491    pub fn is_abstract(&self) -> bool {
1492        self.access_flags & access_flags::ABSTRACT != 0
1493    }
1494}
1495#[allow(dead_code)]
1496#[derive(Debug, Clone)]
1497pub struct JVMAnalysisCache {
1498    pub(super) entries: std::collections::HashMap<String, JVMCacheEntry>,
1499    pub(super) max_size: usize,
1500    pub(super) hits: u64,
1501    pub(super) misses: u64,
1502}
1503impl JVMAnalysisCache {
1504    #[allow(dead_code)]
1505    pub fn new(max_size: usize) -> Self {
1506        JVMAnalysisCache {
1507            entries: std::collections::HashMap::new(),
1508            max_size,
1509            hits: 0,
1510            misses: 0,
1511        }
1512    }
1513    #[allow(dead_code)]
1514    pub fn get(&mut self, key: &str) -> Option<&JVMCacheEntry> {
1515        if self.entries.contains_key(key) {
1516            self.hits += 1;
1517            self.entries.get(key)
1518        } else {
1519            self.misses += 1;
1520            None
1521        }
1522    }
1523    #[allow(dead_code)]
1524    pub fn insert(&mut self, key: String, data: Vec<u8>) {
1525        if self.entries.len() >= self.max_size {
1526            if let Some(oldest) = self.entries.keys().next().cloned() {
1527                self.entries.remove(&oldest);
1528            }
1529        }
1530        self.entries.insert(
1531            key.clone(),
1532            JVMCacheEntry {
1533                key,
1534                data,
1535                timestamp: 0,
1536                valid: true,
1537            },
1538        );
1539    }
1540    #[allow(dead_code)]
1541    pub fn invalidate(&mut self, key: &str) {
1542        if let Some(entry) = self.entries.get_mut(key) {
1543            entry.valid = false;
1544        }
1545    }
1546    #[allow(dead_code)]
1547    pub fn clear(&mut self) {
1548        self.entries.clear();
1549    }
1550    #[allow(dead_code)]
1551    pub fn hit_rate(&self) -> f64 {
1552        let total = self.hits + self.misses;
1553        if total == 0 {
1554            return 0.0;
1555        }
1556        self.hits as f64 / total as f64
1557    }
1558    #[allow(dead_code)]
1559    pub fn size(&self) -> usize {
1560        self.entries.len()
1561    }
1562}
1563#[allow(dead_code)]
1564#[derive(Debug, Clone)]
1565pub struct JVMCacheEntry {
1566    pub key: String,
1567    pub data: Vec<u8>,
1568    pub timestamp: u64,
1569    pub valid: bool,
1570}
1571/// Statistics for JVMExt passes.
1572#[allow(dead_code)]
1573#[derive(Debug, Clone, Default)]
1574pub struct JVMExtPassStats {
1575    pub iterations: usize,
1576    pub changed: bool,
1577    pub nodes_visited: usize,
1578    pub nodes_modified: usize,
1579    pub time_ms: u64,
1580    pub memory_bytes: usize,
1581    pub errors: usize,
1582}
1583impl JVMExtPassStats {
1584    #[allow(dead_code)]
1585    pub fn new() -> Self {
1586        Self::default()
1587    }
1588    #[allow(dead_code)]
1589    pub fn visit(&mut self) {
1590        self.nodes_visited += 1;
1591    }
1592    #[allow(dead_code)]
1593    pub fn modify(&mut self) {
1594        self.nodes_modified += 1;
1595        self.changed = true;
1596    }
1597    #[allow(dead_code)]
1598    pub fn iterate(&mut self) {
1599        self.iterations += 1;
1600    }
1601    #[allow(dead_code)]
1602    pub fn error(&mut self) {
1603        self.errors += 1;
1604    }
1605    #[allow(dead_code)]
1606    pub fn efficiency(&self) -> f64 {
1607        if self.nodes_visited == 0 {
1608            0.0
1609        } else {
1610            self.nodes_modified as f64 / self.nodes_visited as f64
1611        }
1612    }
1613    #[allow(dead_code)]
1614    pub fn merge(&mut self, o: &JVMExtPassStats) {
1615        self.iterations += o.iterations;
1616        self.changed |= o.changed;
1617        self.nodes_visited += o.nodes_visited;
1618        self.nodes_modified += o.nodes_modified;
1619        self.time_ms += o.time_ms;
1620        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1621        self.errors += o.errors;
1622    }
1623}
1624/// Dependency graph for JVMExt.
1625#[allow(dead_code)]
1626#[derive(Debug, Clone)]
1627pub struct JVMExtDepGraph {
1628    pub(super) n: usize,
1629    pub(super) adj: Vec<Vec<usize>>,
1630    pub(super) rev: Vec<Vec<usize>>,
1631    pub(super) edge_count: usize,
1632}
1633impl JVMExtDepGraph {
1634    #[allow(dead_code)]
1635    pub fn new(n: usize) -> Self {
1636        Self {
1637            n,
1638            adj: vec![Vec::new(); n],
1639            rev: vec![Vec::new(); n],
1640            edge_count: 0,
1641        }
1642    }
1643    #[allow(dead_code)]
1644    pub fn add_edge(&mut self, from: usize, to: usize) {
1645        if from < self.n && to < self.n {
1646            if !self.adj[from].contains(&to) {
1647                self.adj[from].push(to);
1648                self.rev[to].push(from);
1649                self.edge_count += 1;
1650            }
1651        }
1652    }
1653    #[allow(dead_code)]
1654    pub fn succs(&self, n: usize) -> &[usize] {
1655        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1656    }
1657    #[allow(dead_code)]
1658    pub fn preds(&self, n: usize) -> &[usize] {
1659        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1660    }
1661    #[allow(dead_code)]
1662    pub fn topo_sort(&self) -> Option<Vec<usize>> {
1663        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
1664        let mut q: std::collections::VecDeque<usize> =
1665            (0..self.n).filter(|&i| deg[i] == 0).collect();
1666        let mut out = Vec::with_capacity(self.n);
1667        while let Some(u) = q.pop_front() {
1668            out.push(u);
1669            for &v in &self.adj[u] {
1670                deg[v] -= 1;
1671                if deg[v] == 0 {
1672                    q.push_back(v);
1673                }
1674            }
1675        }
1676        if out.len() == self.n {
1677            Some(out)
1678        } else {
1679            None
1680        }
1681    }
1682    #[allow(dead_code)]
1683    pub fn has_cycle(&self) -> bool {
1684        self.topo_sort().is_none()
1685    }
1686    #[allow(dead_code)]
1687    pub fn reachable(&self, start: usize) -> Vec<usize> {
1688        let mut vis = vec![false; self.n];
1689        let mut stk = vec![start];
1690        let mut out = Vec::new();
1691        while let Some(u) = stk.pop() {
1692            if u < self.n && !vis[u] {
1693                vis[u] = true;
1694                out.push(u);
1695                for &v in &self.adj[u] {
1696                    if !vis[v] {
1697                        stk.push(v);
1698                    }
1699                }
1700            }
1701        }
1702        out
1703    }
1704    #[allow(dead_code)]
1705    pub fn scc(&self) -> Vec<Vec<usize>> {
1706        let mut visited = vec![false; self.n];
1707        let mut order = Vec::new();
1708        for i in 0..self.n {
1709            if !visited[i] {
1710                let mut stk = vec![(i, 0usize)];
1711                while let Some((u, idx)) = stk.last_mut() {
1712                    if !visited[*u] {
1713                        visited[*u] = true;
1714                    }
1715                    if *idx < self.adj[*u].len() {
1716                        let v = self.adj[*u][*idx];
1717                        *idx += 1;
1718                        if !visited[v] {
1719                            stk.push((v, 0));
1720                        }
1721                    } else {
1722                        order.push(*u);
1723                        stk.pop();
1724                    }
1725                }
1726            }
1727        }
1728        let mut comp = vec![usize::MAX; self.n];
1729        let mut components: Vec<Vec<usize>> = Vec::new();
1730        for &start in order.iter().rev() {
1731            if comp[start] == usize::MAX {
1732                let cid = components.len();
1733                let mut component = Vec::new();
1734                let mut stk = vec![start];
1735                while let Some(u) = stk.pop() {
1736                    if comp[u] == usize::MAX {
1737                        comp[u] = cid;
1738                        component.push(u);
1739                        for &v in &self.rev[u] {
1740                            if comp[v] == usize::MAX {
1741                                stk.push(v);
1742                            }
1743                        }
1744                    }
1745                }
1746                components.push(component);
1747            }
1748        }
1749        components
1750    }
1751    #[allow(dead_code)]
1752    pub fn node_count(&self) -> usize {
1753        self.n
1754    }
1755    #[allow(dead_code)]
1756    pub fn edge_count(&self) -> usize {
1757        self.edge_count
1758    }
1759}
1760/// One row of the JVM exception table.
1761#[derive(Debug, Clone)]
1762pub struct ExceptionEntry {
1763    /// Start of the try region (instruction index, not byte offset).
1764    pub start: u16,
1765    /// Exclusive end of the try region.
1766    pub end: u16,
1767    /// Handler instruction index.
1768    pub handler: u16,
1769    /// Catch type class name (`None` → finally / catch-all).
1770    pub catch_type: Option<String>,
1771}
1772/// Method descriptor helper.
1773#[derive(Debug, Clone)]
1774pub struct MethodDescriptor {
1775    /// Parameter types.
1776    pub params: Vec<JvmType>,
1777    /// Return type.
1778    pub return_type: JvmType,
1779}
1780impl MethodDescriptor {
1781    pub fn new(params: Vec<JvmType>, return_type: JvmType) -> Self {
1782        MethodDescriptor {
1783            params,
1784            return_type,
1785        }
1786    }
1787}
1788/// JVM field/method descriptor types.
1789#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1790pub enum JvmType {
1791    /// `B` — signed 8-bit integer
1792    Byte,
1793    /// `S` — signed 16-bit integer
1794    Short,
1795    /// `I` — signed 32-bit integer
1796    Int,
1797    /// `J` — signed 64-bit integer
1798    Long,
1799    /// `F` — 32-bit IEEE 754 float
1800    Float,
1801    /// `D` — 64-bit IEEE 754 double
1802    Double,
1803    /// `Z` — boolean
1804    Boolean,
1805    /// `C` — UTF-16 code unit
1806    Char,
1807    /// `V` — no return value
1808    Void,
1809    /// `Lclass/name;` — object reference
1810    Object(String),
1811    /// `[T` — array of T
1812    Array(Box<JvmType>),
1813    /// Erased generic type variable (represented as `java/lang/Object`)
1814    Generic(String),
1815}
1816impl JvmType {
1817    /// Returns the JVM field descriptor string (e.g. `"I"`, `"[Ljava/lang/String;"`).
1818    pub fn descriptor(&self) -> String {
1819        match self {
1820            JvmType::Byte => "B".to_string(),
1821            JvmType::Short => "S".to_string(),
1822            JvmType::Int => "I".to_string(),
1823            JvmType::Long => "J".to_string(),
1824            JvmType::Float => "F".to_string(),
1825            JvmType::Double => "D".to_string(),
1826            JvmType::Boolean => "Z".to_string(),
1827            JvmType::Char => "C".to_string(),
1828            JvmType::Void => "V".to_string(),
1829            JvmType::Object(cls) => format!("L{};", cls),
1830            JvmType::Array(inner) => format!("[{}", inner.descriptor()),
1831            JvmType::Generic(_) => "Ljava/lang/Object;".to_string(),
1832        }
1833    }
1834    /// Returns the size in JVM local-variable / stack slots (1 or 2).
1835    pub fn slot_size(&self) -> usize {
1836        match self {
1837            JvmType::Long | JvmType::Double => 2,
1838            _ => 1,
1839        }
1840    }
1841    /// `true` for the two "wide" primitive types (Long, Double).
1842    pub fn is_wide(&self) -> bool {
1843        self.slot_size() == 2
1844    }
1845    /// `true` for reference types (Object, Array, Generic).
1846    pub fn is_reference(&self) -> bool {
1847        matches!(
1848            self,
1849            JvmType::Object(_) | JvmType::Array(_) | JvmType::Generic(_)
1850        )
1851    }
1852    /// `true` for integer-category types (Byte, Short, Int, Boolean, Char).
1853    pub fn is_int_category(&self) -> bool {
1854        matches!(
1855            self,
1856            JvmType::Byte | JvmType::Short | JvmType::Int | JvmType::Boolean | JvmType::Char
1857        )
1858    }
1859}
1860#[allow(dead_code)]
1861pub struct JVMPassRegistry {
1862    pub(super) configs: Vec<JVMPassConfig>,
1863    pub(super) stats: std::collections::HashMap<String, JVMPassStats>,
1864}
1865impl JVMPassRegistry {
1866    #[allow(dead_code)]
1867    pub fn new() -> Self {
1868        JVMPassRegistry {
1869            configs: Vec::new(),
1870            stats: std::collections::HashMap::new(),
1871        }
1872    }
1873    #[allow(dead_code)]
1874    pub fn register(&mut self, config: JVMPassConfig) {
1875        self.stats
1876            .insert(config.pass_name.clone(), JVMPassStats::new());
1877        self.configs.push(config);
1878    }
1879    #[allow(dead_code)]
1880    pub fn enabled_passes(&self) -> Vec<&JVMPassConfig> {
1881        self.configs.iter().filter(|c| c.enabled).collect()
1882    }
1883    #[allow(dead_code)]
1884    pub fn get_stats(&self, name: &str) -> Option<&JVMPassStats> {
1885        self.stats.get(name)
1886    }
1887    #[allow(dead_code)]
1888    pub fn total_passes(&self) -> usize {
1889        self.configs.len()
1890    }
1891    #[allow(dead_code)]
1892    pub fn enabled_count(&self) -> usize {
1893        self.enabled_passes().len()
1894    }
1895    #[allow(dead_code)]
1896    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1897        if let Some(stats) = self.stats.get_mut(name) {
1898            stats.record_run(changes, time_ms, iter);
1899        }
1900    }
1901}
1902/// Configuration for the JVM backend.
1903#[derive(Debug, Clone)]
1904pub struct JvmConfig {
1905    /// Package prefix (e.g. `"com.example"`).
1906    pub package: String,
1907    /// Java class-file major version (default 65 = Java 21).
1908    pub class_version: u16,
1909    /// Emit debug line-number tables.
1910    pub emit_line_numbers: bool,
1911    /// Whether to generate sealed-interface hierarchies for ADT types.
1912    pub sealed_adt: bool,
1913}
1914/// Minimal constant pool builder.
1915#[derive(Debug, Clone, Default)]
1916pub struct ConstantPool {
1917    pub(super) entries: Vec<ConstantPoolEntry>,
1918}
1919impl ConstantPool {
1920    pub fn new() -> Self {
1921        ConstantPool {
1922            entries: Vec::new(),
1923        }
1924    }
1925    /// Add an entry and return its 1-based index.
1926    pub fn add(&mut self, entry: ConstantPoolEntry) -> u16 {
1927        self.entries.push(entry);
1928        self.entries.len() as u16
1929    }
1930    /// Find or add a CONSTANT_Utf8 entry, returning its index.
1931    pub fn utf8(&mut self, s: &str) -> u16 {
1932        for (i, e) in self.entries.iter().enumerate() {
1933            if let ConstantPoolEntry::Utf8(v) = e {
1934                if v == s {
1935                    return (i + 1) as u16;
1936                }
1937            }
1938        }
1939        self.add(ConstantPoolEntry::Utf8(s.to_string()))
1940    }
1941    /// Find or add a CONSTANT_Class entry.
1942    pub fn class(&mut self, name: &str) -> u16 {
1943        let name_index = self.utf8(name);
1944        for (i, e) in self.entries.iter().enumerate() {
1945            if let ConstantPoolEntry::Class { name_index: ni } = e {
1946                if *ni == name_index {
1947                    return (i + 1) as u16;
1948                }
1949            }
1950        }
1951        self.add(ConstantPoolEntry::Class { name_index })
1952    }
1953    /// All constant pool entries (1-indexed in the class file).
1954    pub fn entries(&self) -> &[ConstantPoolEntry] {
1955        &self.entries
1956    }
1957}
1958#[allow(dead_code)]
1959#[derive(Debug, Clone)]
1960pub struct JVMWorklist {
1961    pub(super) items: std::collections::VecDeque<u32>,
1962    pub(super) in_worklist: std::collections::HashSet<u32>,
1963}
1964impl JVMWorklist {
1965    #[allow(dead_code)]
1966    pub fn new() -> Self {
1967        JVMWorklist {
1968            items: std::collections::VecDeque::new(),
1969            in_worklist: std::collections::HashSet::new(),
1970        }
1971    }
1972    #[allow(dead_code)]
1973    pub fn push(&mut self, item: u32) -> bool {
1974        if self.in_worklist.insert(item) {
1975            self.items.push_back(item);
1976            true
1977        } else {
1978            false
1979        }
1980    }
1981    #[allow(dead_code)]
1982    pub fn pop(&mut self) -> Option<u32> {
1983        let item = self.items.pop_front()?;
1984        self.in_worklist.remove(&item);
1985        Some(item)
1986    }
1987    #[allow(dead_code)]
1988    pub fn is_empty(&self) -> bool {
1989        self.items.is_empty()
1990    }
1991    #[allow(dead_code)]
1992    pub fn len(&self) -> usize {
1993        self.items.len()
1994    }
1995    #[allow(dead_code)]
1996    pub fn contains(&self, item: u32) -> bool {
1997        self.in_worklist.contains(&item)
1998    }
1999}
2000#[allow(dead_code)]
2001#[derive(Debug, Clone)]
2002pub struct JVMDepGraph {
2003    pub(super) nodes: Vec<u32>,
2004    pub(super) edges: Vec<(u32, u32)>,
2005}
2006impl JVMDepGraph {
2007    #[allow(dead_code)]
2008    pub fn new() -> Self {
2009        JVMDepGraph {
2010            nodes: Vec::new(),
2011            edges: Vec::new(),
2012        }
2013    }
2014    #[allow(dead_code)]
2015    pub fn add_node(&mut self, id: u32) {
2016        if !self.nodes.contains(&id) {
2017            self.nodes.push(id);
2018        }
2019    }
2020    #[allow(dead_code)]
2021    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
2022        self.add_node(dep);
2023        self.add_node(dependent);
2024        self.edges.push((dep, dependent));
2025    }
2026    #[allow(dead_code)]
2027    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
2028        self.edges
2029            .iter()
2030            .filter(|(d, _)| *d == node)
2031            .map(|(_, dep)| *dep)
2032            .collect()
2033    }
2034    #[allow(dead_code)]
2035    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
2036        self.edges
2037            .iter()
2038            .filter(|(_, dep)| *dep == node)
2039            .map(|(d, _)| *d)
2040            .collect()
2041    }
2042    #[allow(dead_code)]
2043    pub fn topological_sort(&self) -> Vec<u32> {
2044        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
2045        for &n in &self.nodes {
2046            in_degree.insert(n, 0);
2047        }
2048        for (_, dep) in &self.edges {
2049            *in_degree.entry(*dep).or_insert(0) += 1;
2050        }
2051        let mut queue: std::collections::VecDeque<u32> = self
2052            .nodes
2053            .iter()
2054            .filter(|&&n| in_degree[&n] == 0)
2055            .copied()
2056            .collect();
2057        let mut result = Vec::new();
2058        while let Some(node) = queue.pop_front() {
2059            result.push(node);
2060            for dep in self.dependents_of(node) {
2061                let cnt = in_degree.entry(dep).or_insert(0);
2062                *cnt = cnt.saturating_sub(1);
2063                if *cnt == 0 {
2064                    queue.push_back(dep);
2065                }
2066            }
2067        }
2068        result
2069    }
2070    #[allow(dead_code)]
2071    pub fn has_cycle(&self) -> bool {
2072        self.topological_sort().len() < self.nodes.len()
2073    }
2074}
2075#[allow(dead_code)]
2076#[derive(Debug, Clone)]
2077pub struct JVMDominatorTree {
2078    pub idom: Vec<Option<u32>>,
2079    pub dom_children: Vec<Vec<u32>>,
2080    pub dom_depth: Vec<u32>,
2081}
2082impl JVMDominatorTree {
2083    #[allow(dead_code)]
2084    pub fn new(size: usize) -> Self {
2085        JVMDominatorTree {
2086            idom: vec![None; size],
2087            dom_children: vec![Vec::new(); size],
2088            dom_depth: vec![0; size],
2089        }
2090    }
2091    #[allow(dead_code)]
2092    pub fn set_idom(&mut self, node: usize, idom: u32) {
2093        self.idom[node] = Some(idom);
2094    }
2095    #[allow(dead_code)]
2096    pub fn dominates(&self, a: usize, b: usize) -> bool {
2097        if a == b {
2098            return true;
2099        }
2100        let mut cur = b;
2101        loop {
2102            match self.idom[cur] {
2103                Some(parent) if parent as usize == a => return true,
2104                Some(parent) if parent as usize == cur => return false,
2105                Some(parent) => cur = parent as usize,
2106                None => return false,
2107            }
2108        }
2109    }
2110    #[allow(dead_code)]
2111    pub fn depth(&self, node: usize) -> u32 {
2112        self.dom_depth.get(node).copied().unwrap_or(0)
2113    }
2114}
2115/// A field (instance or static) inside a JVM class.
2116#[derive(Debug, Clone)]
2117pub struct JvmField {
2118    /// Field name.
2119    pub name: String,
2120    /// Field descriptor (e.g. `"I"`, `"Ljava/lang/String;"`).
2121    pub descriptor: String,
2122    /// Access flags bitmask.
2123    pub access_flags: u16,
2124    /// Optional constant-value attribute (for static final fields).
2125    pub constant_value: Option<ConstantPoolEntry>,
2126}
2127impl JvmField {
2128    pub fn new(name: &str, ty: &JvmType, access_flags: u16) -> Self {
2129        JvmField {
2130            name: name.to_string(),
2131            descriptor: ty.descriptor(),
2132            access_flags,
2133            constant_value: None,
2134        }
2135    }
2136}
2137#[allow(dead_code)]
2138#[derive(Debug, Clone, Default)]
2139pub struct JVMPassStats {
2140    pub total_runs: u32,
2141    pub successful_runs: u32,
2142    pub total_changes: u64,
2143    pub time_ms: u64,
2144    pub iterations_used: u32,
2145}
2146impl JVMPassStats {
2147    #[allow(dead_code)]
2148    pub fn new() -> Self {
2149        Self::default()
2150    }
2151    #[allow(dead_code)]
2152    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
2153        self.total_runs += 1;
2154        self.successful_runs += 1;
2155        self.total_changes += changes;
2156        self.time_ms += time_ms;
2157        self.iterations_used = iterations;
2158    }
2159    #[allow(dead_code)]
2160    pub fn average_changes_per_run(&self) -> f64 {
2161        if self.total_runs == 0 {
2162            return 0.0;
2163        }
2164        self.total_changes as f64 / self.total_runs as f64
2165    }
2166    #[allow(dead_code)]
2167    pub fn success_rate(&self) -> f64 {
2168        if self.total_runs == 0 {
2169            return 0.0;
2170        }
2171        self.successful_runs as f64 / self.total_runs as f64
2172    }
2173    #[allow(dead_code)]
2174    pub fn format_summary(&self) -> String {
2175        format!(
2176            "Runs: {}/{}, Changes: {}, Time: {}ms",
2177            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
2178        )
2179    }
2180}