1use 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#[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#[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#[derive(Debug, Clone)]
239pub enum JvmCodegenError {
240 Unsupported(String),
242 UnknownVar(String),
244 Internal(String),
246}
247pub struct JvmBackend {
249 pub(super) config: JvmConfig,
250 pub(super) label_counter: u32,
252 pub(super) locals: HashMap<String, u16>,
254 pub(super) next_local: u16,
256}
257impl JvmBackend {
258 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ¶m.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 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 ¶m.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 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 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 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#[derive(Debug, Clone)]
641pub struct JvmInstruction {
642 pub opcode: JvmOpcode,
644 pub line: Option<u32>,
646}
647impl JvmInstruction {
648 pub fn new(opcode: JvmOpcode) -> Self {
650 JvmInstruction { opcode, line: None }
651 }
652 pub fn with_line(opcode: JvmOpcode, line: u32) -> Self {
654 JvmInstruction {
655 opcode,
656 line: Some(line),
657 }
658 }
659}
660#[derive(Debug, Clone, PartialEq)]
665pub enum JvmOpcode {
666 AconstNull,
668 Iconst(i32),
670 Lconst(i64),
672 Fconst(f32),
674 Dconst(f64),
676 Bipush(i8),
678 Sipush(i16),
680 Ldc(u16),
682 Iload(u16),
684 Lload(u16),
686 Fload(u16),
688 Dload(u16),
690 Aload(u16),
692 Istore(u16),
694 Lstore(u16),
696 Fstore(u16),
698 Dstore(u16),
700 Astore(u16),
702 Iaload,
704 Aaload,
706 Iastore,
708 Aastore,
710 Pop,
712 Pop2,
714 Dup,
716 Swap,
718 Iadd,
720 Isub,
722 Imul,
724 Idiv,
726 Irem,
728 Ineg,
730 Ladd,
732 Lsub,
734 Lmul,
736 Ldiv,
738 Dadd,
740 Dsub,
742 Dmul,
744 Ddiv,
746 I2l,
748 I2d,
750 L2i,
752 D2i,
754 IfIcmpeq(i16),
756 IfIcmpne(i16),
758 IfIcmplt(i16),
760 IfIcmpge(i16),
762 IfIcmpgt(i16),
764 IfIcmple(i16),
766 Ifnull(i16),
768 Ifnonnull(i16),
770 Ifeq(i16),
772 Ifne(i16),
774 Lcmp,
776 Goto(i16),
778 Return_,
780 Ireturn,
782 Lreturn,
784 Freturn,
786 Dreturn,
788 Areturn,
790 Athrow,
792 Getfield {
794 class: String,
795 name: String,
796 descriptor: String,
797 },
798 Putfield {
800 class: String,
801 name: String,
802 descriptor: String,
803 },
804 Getstatic {
806 class: String,
807 name: String,
808 descriptor: String,
809 },
810 Putstatic {
812 class: String,
813 name: String,
814 descriptor: String,
815 },
816 Invokevirtual {
818 class: String,
819 name: String,
820 descriptor: String,
821 },
822 Invokeinterface {
824 class: String,
825 name: String,
826 descriptor: String,
827 count: u8,
828 },
829 Invokespecial {
831 class: String,
832 name: String,
833 descriptor: String,
834 },
835 Invokestatic {
837 class: String,
838 name: String,
839 descriptor: String,
840 },
841 New(String),
843 Newarray(JvmType),
845 Anewarray(String),
847 Arraylength,
849 Instanceof(String),
851 Checkcast(String),
853 Label(String),
855 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#[derive(Debug, Clone, PartialEq)]
883pub enum ConstantPoolEntry {
884 Utf8(String),
886 Integer(i32),
888 Long(i64),
890 Float(f32),
892 Double(f64),
894 Class { name_index: u16 },
896 StringRef { string_index: u16 },
898 Fieldref {
900 class_index: u16,
901 name_and_type_index: u16,
902 },
903 Methodref {
905 class_index: u16,
906 name_and_type_index: u16,
907 },
908 InterfaceMethodref {
910 class_index: u16,
911 name_and_type_index: u16,
912 },
913 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#[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#[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#[derive(Debug, Clone)]
1152pub struct JvmClass {
1153 pub name: String,
1155 pub superclass: String,
1157 pub interfaces: Vec<String>,
1159 pub fields: Vec<JvmField>,
1161 pub methods: Vec<JvmMethod>,
1163 pub access_flags: u16,
1165 pub constant_pool: ConstantPool,
1167 pub major_version: u16,
1169 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 pub fn add_field(&mut self, field: JvmField) {
1188 self.fields.push(field);
1189 }
1190 pub fn add_method(&mut self, method: JvmMethod) {
1192 self.methods.push(method);
1193 }
1194 pub fn add_interface(&mut self, iface: &str) {
1196 self.interfaces.push(iface.to_string());
1197 }
1198 pub fn set_superclass(&mut self, super_name: &str) {
1200 self.superclass = super_name.to_string();
1201 }
1202 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#[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#[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#[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#[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#[derive(Debug, Clone)]
1450pub struct JvmMethod {
1451 pub name: String,
1453 pub descriptor: String,
1455 pub access_flags: u16,
1457 pub code: Vec<JvmInstruction>,
1459 pub max_stack: u16,
1461 pub max_locals: u16,
1463 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 pub fn add_exception(&mut self, entry: ExceptionEntry) {
1488 self.exceptions.push(entry);
1489 }
1490 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#[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#[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#[derive(Debug, Clone)]
1762pub struct ExceptionEntry {
1763 pub start: u16,
1765 pub end: u16,
1767 pub handler: u16,
1769 pub catch_type: Option<String>,
1771}
1772#[derive(Debug, Clone)]
1774pub struct MethodDescriptor {
1775 pub params: Vec<JvmType>,
1777 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1790pub enum JvmType {
1791 Byte,
1793 Short,
1795 Int,
1797 Long,
1799 Float,
1801 Double,
1803 Boolean,
1805 Char,
1807 Void,
1809 Object(String),
1811 Array(Box<JvmType>),
1813 Generic(String),
1815}
1816impl JvmType {
1817 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 pub fn slot_size(&self) -> usize {
1836 match self {
1837 JvmType::Long | JvmType::Double => 2,
1838 _ => 1,
1839 }
1840 }
1841 pub fn is_wide(&self) -> bool {
1843 self.slot_size() == 2
1844 }
1845 pub fn is_reference(&self) -> bool {
1847 matches!(
1848 self,
1849 JvmType::Object(_) | JvmType::Array(_) | JvmType::Generic(_)
1850 )
1851 }
1852 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#[derive(Debug, Clone)]
1904pub struct JvmConfig {
1905 pub package: String,
1907 pub class_version: u16,
1909 pub emit_line_numbers: bool,
1911 pub sealed_adt: bool,
1913}
1914#[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 pub fn add(&mut self, entry: ConstantPoolEntry) -> u16 {
1927 self.entries.push(entry);
1928 self.entries.len() as u16
1929 }
1930 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 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 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#[derive(Debug, Clone)]
2117pub struct JvmField {
2118 pub name: String,
2120 pub descriptor: String,
2122 pub access_flags: u16,
2124 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}