1use crate::lcnf::*;
6use std::collections::HashSet;
7
8use super::functions::KOTLIN_KEYWORDS;
9
10use super::functions::*;
11use std::collections::{HashMap, VecDeque};
12
13#[allow(dead_code)]
15#[derive(Debug)]
16pub struct KotlinExtCache {
17 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
18 pub(super) cap: usize,
19 pub(super) total_hits: u64,
20 pub(super) total_misses: u64,
21}
22impl KotlinExtCache {
23 #[allow(dead_code)]
24 pub fn new(cap: usize) -> Self {
25 Self {
26 entries: Vec::new(),
27 cap,
28 total_hits: 0,
29 total_misses: 0,
30 }
31 }
32 #[allow(dead_code)]
33 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
34 for e in self.entries.iter_mut() {
35 if e.0 == key && e.2 {
36 e.3 += 1;
37 self.total_hits += 1;
38 return Some(&e.1);
39 }
40 }
41 self.total_misses += 1;
42 None
43 }
44 #[allow(dead_code)]
45 pub fn put(&mut self, key: u64, data: Vec<u8>) {
46 if self.entries.len() >= self.cap {
47 self.entries.retain(|e| e.2);
48 if self.entries.len() >= self.cap {
49 self.entries.remove(0);
50 }
51 }
52 self.entries.push((key, data, true, 0));
53 }
54 #[allow(dead_code)]
55 pub fn invalidate(&mut self) {
56 for e in self.entries.iter_mut() {
57 e.2 = false;
58 }
59 }
60 #[allow(dead_code)]
61 pub fn hit_rate(&self) -> f64 {
62 let t = self.total_hits + self.total_misses;
63 if t == 0 {
64 0.0
65 } else {
66 self.total_hits as f64 / t as f64
67 }
68 }
69 #[allow(dead_code)]
70 pub fn live_count(&self) -> usize {
71 self.entries.iter().filter(|e| e.2).count()
72 }
73}
74#[derive(Debug, Clone, PartialEq)]
76pub struct KotlinWhenBranch {
77 pub condition: KotlinExpr,
78 pub body: KotlinExpr,
79}
80#[allow(dead_code)]
82#[derive(Debug, Clone)]
83pub struct KotlinExtPassConfig {
84 pub name: String,
85 pub phase: KotlinExtPassPhase,
86 pub enabled: bool,
87 pub max_iterations: usize,
88 pub debug: u32,
89 pub timeout_ms: Option<u64>,
90}
91impl KotlinExtPassConfig {
92 #[allow(dead_code)]
93 pub fn new(name: impl Into<String>) -> Self {
94 Self {
95 name: name.into(),
96 phase: KotlinExtPassPhase::Middle,
97 enabled: true,
98 max_iterations: 100,
99 debug: 0,
100 timeout_ms: None,
101 }
102 }
103 #[allow(dead_code)]
104 pub fn with_phase(mut self, phase: KotlinExtPassPhase) -> Self {
105 self.phase = phase;
106 self
107 }
108 #[allow(dead_code)]
109 pub fn with_max_iter(mut self, n: usize) -> Self {
110 self.max_iterations = n;
111 self
112 }
113 #[allow(dead_code)]
114 pub fn with_debug(mut self, d: u32) -> Self {
115 self.debug = d;
116 self
117 }
118 #[allow(dead_code)]
119 pub fn disabled(mut self) -> Self {
120 self.enabled = false;
121 self
122 }
123 #[allow(dead_code)]
124 pub fn with_timeout(mut self, ms: u64) -> Self {
125 self.timeout_ms = Some(ms);
126 self
127 }
128 #[allow(dead_code)]
129 pub fn is_debug_enabled(&self) -> bool {
130 self.debug > 0
131 }
132}
133#[allow(dead_code)]
134#[derive(Debug, Clone)]
135pub struct KtDepGraph {
136 pub(super) nodes: Vec<u32>,
137 pub(super) edges: Vec<(u32, u32)>,
138}
139impl KtDepGraph {
140 #[allow(dead_code)]
141 pub fn new() -> Self {
142 KtDepGraph {
143 nodes: Vec::new(),
144 edges: Vec::new(),
145 }
146 }
147 #[allow(dead_code)]
148 pub fn add_node(&mut self, id: u32) {
149 if !self.nodes.contains(&id) {
150 self.nodes.push(id);
151 }
152 }
153 #[allow(dead_code)]
154 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
155 self.add_node(dep);
156 self.add_node(dependent);
157 self.edges.push((dep, dependent));
158 }
159 #[allow(dead_code)]
160 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
161 self.edges
162 .iter()
163 .filter(|(d, _)| *d == node)
164 .map(|(_, dep)| *dep)
165 .collect()
166 }
167 #[allow(dead_code)]
168 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
169 self.edges
170 .iter()
171 .filter(|(_, dep)| *dep == node)
172 .map(|(d, _)| *d)
173 .collect()
174 }
175 #[allow(dead_code)]
176 pub fn topological_sort(&self) -> Vec<u32> {
177 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
178 for &n in &self.nodes {
179 in_degree.insert(n, 0);
180 }
181 for (_, dep) in &self.edges {
182 *in_degree.entry(*dep).or_insert(0) += 1;
183 }
184 let mut queue: std::collections::VecDeque<u32> = self
185 .nodes
186 .iter()
187 .filter(|&&n| in_degree[&n] == 0)
188 .copied()
189 .collect();
190 let mut result = Vec::new();
191 while let Some(node) = queue.pop_front() {
192 result.push(node);
193 for dep in self.dependents_of(node) {
194 let cnt = in_degree.entry(dep).or_insert(0);
195 *cnt = cnt.saturating_sub(1);
196 if *cnt == 0 {
197 queue.push_back(dep);
198 }
199 }
200 }
201 result
202 }
203 #[allow(dead_code)]
204 pub fn has_cycle(&self) -> bool {
205 self.topological_sort().len() < self.nodes.len()
206 }
207}
208#[allow(dead_code)]
210#[derive(Debug, Clone)]
211pub struct KotlinExtDomTree {
212 pub(super) idom: Vec<Option<usize>>,
213 pub(super) children: Vec<Vec<usize>>,
214 pub(super) depth: Vec<usize>,
215}
216impl KotlinExtDomTree {
217 #[allow(dead_code)]
218 pub fn new(n: usize) -> Self {
219 Self {
220 idom: vec![None; n],
221 children: vec![Vec::new(); n],
222 depth: vec![0; n],
223 }
224 }
225 #[allow(dead_code)]
226 pub fn set_idom(&mut self, node: usize, dom: usize) {
227 if node < self.idom.len() {
228 self.idom[node] = Some(dom);
229 if dom < self.children.len() {
230 self.children[dom].push(node);
231 }
232 self.depth[node] = if dom < self.depth.len() {
233 self.depth[dom] + 1
234 } else {
235 1
236 };
237 }
238 }
239 #[allow(dead_code)]
240 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
241 if a == b {
242 return true;
243 }
244 let n = self.idom.len();
245 for _ in 0..n {
246 match self.idom.get(b).copied().flatten() {
247 None => return false,
248 Some(p) if p == a => return true,
249 Some(p) if p == b => return false,
250 Some(p) => b = p,
251 }
252 }
253 false
254 }
255 #[allow(dead_code)]
256 pub fn children_of(&self, n: usize) -> &[usize] {
257 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
258 }
259 #[allow(dead_code)]
260 pub fn depth_of(&self, n: usize) -> usize {
261 self.depth.get(n).copied().unwrap_or(0)
262 }
263 #[allow(dead_code)]
264 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
265 let n = self.idom.len();
266 for _ in 0..(2 * n) {
267 if a == b {
268 return a;
269 }
270 if self.depth_of(a) > self.depth_of(b) {
271 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
272 } else {
273 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
274 }
275 }
276 0
277 }
278}
279#[allow(dead_code)]
281#[derive(Debug, Default)]
282pub struct KotlinExtPassRegistry {
283 pub(super) configs: Vec<KotlinExtPassConfig>,
284 pub(super) stats: Vec<KotlinExtPassStats>,
285}
286impl KotlinExtPassRegistry {
287 #[allow(dead_code)]
288 pub fn new() -> Self {
289 Self::default()
290 }
291 #[allow(dead_code)]
292 pub fn register(&mut self, c: KotlinExtPassConfig) {
293 self.stats.push(KotlinExtPassStats::new());
294 self.configs.push(c);
295 }
296 #[allow(dead_code)]
297 pub fn len(&self) -> usize {
298 self.configs.len()
299 }
300 #[allow(dead_code)]
301 pub fn is_empty(&self) -> bool {
302 self.configs.is_empty()
303 }
304 #[allow(dead_code)]
305 pub fn get(&self, i: usize) -> Option<&KotlinExtPassConfig> {
306 self.configs.get(i)
307 }
308 #[allow(dead_code)]
309 pub fn get_stats(&self, i: usize) -> Option<&KotlinExtPassStats> {
310 self.stats.get(i)
311 }
312 #[allow(dead_code)]
313 pub fn enabled_passes(&self) -> Vec<&KotlinExtPassConfig> {
314 self.configs.iter().filter(|c| c.enabled).collect()
315 }
316 #[allow(dead_code)]
317 pub fn passes_in_phase(&self, ph: &KotlinExtPassPhase) -> Vec<&KotlinExtPassConfig> {
318 self.configs
319 .iter()
320 .filter(|c| c.enabled && &c.phase == ph)
321 .collect()
322 }
323 #[allow(dead_code)]
324 pub fn total_nodes_visited(&self) -> usize {
325 self.stats.iter().map(|s| s.nodes_visited).sum()
326 }
327 #[allow(dead_code)]
328 pub fn any_changed(&self) -> bool {
329 self.stats.iter().any(|s| s.changed)
330 }
331}
332#[allow(dead_code)]
334#[derive(Debug, Clone)]
335pub struct KotlinExtDepGraph {
336 pub(super) n: usize,
337 pub(super) adj: Vec<Vec<usize>>,
338 pub(super) rev: Vec<Vec<usize>>,
339 pub(super) edge_count: usize,
340}
341impl KotlinExtDepGraph {
342 #[allow(dead_code)]
343 pub fn new(n: usize) -> Self {
344 Self {
345 n,
346 adj: vec![Vec::new(); n],
347 rev: vec![Vec::new(); n],
348 edge_count: 0,
349 }
350 }
351 #[allow(dead_code)]
352 pub fn add_edge(&mut self, from: usize, to: usize) {
353 if from < self.n && to < self.n {
354 if !self.adj[from].contains(&to) {
355 self.adj[from].push(to);
356 self.rev[to].push(from);
357 self.edge_count += 1;
358 }
359 }
360 }
361 #[allow(dead_code)]
362 pub fn succs(&self, n: usize) -> &[usize] {
363 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
364 }
365 #[allow(dead_code)]
366 pub fn preds(&self, n: usize) -> &[usize] {
367 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
368 }
369 #[allow(dead_code)]
370 pub fn topo_sort(&self) -> Option<Vec<usize>> {
371 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
372 let mut q: std::collections::VecDeque<usize> =
373 (0..self.n).filter(|&i| deg[i] == 0).collect();
374 let mut out = Vec::with_capacity(self.n);
375 while let Some(u) = q.pop_front() {
376 out.push(u);
377 for &v in &self.adj[u] {
378 deg[v] -= 1;
379 if deg[v] == 0 {
380 q.push_back(v);
381 }
382 }
383 }
384 if out.len() == self.n {
385 Some(out)
386 } else {
387 None
388 }
389 }
390 #[allow(dead_code)]
391 pub fn has_cycle(&self) -> bool {
392 self.topo_sort().is_none()
393 }
394 #[allow(dead_code)]
395 pub fn reachable(&self, start: usize) -> Vec<usize> {
396 let mut vis = vec![false; self.n];
397 let mut stk = vec![start];
398 let mut out = Vec::new();
399 while let Some(u) = stk.pop() {
400 if u < self.n && !vis[u] {
401 vis[u] = true;
402 out.push(u);
403 for &v in &self.adj[u] {
404 if !vis[v] {
405 stk.push(v);
406 }
407 }
408 }
409 }
410 out
411 }
412 #[allow(dead_code)]
413 pub fn scc(&self) -> Vec<Vec<usize>> {
414 let mut visited = vec![false; self.n];
415 let mut order = Vec::new();
416 for i in 0..self.n {
417 if !visited[i] {
418 let mut stk = vec![(i, 0usize)];
419 while let Some((u, idx)) = stk.last_mut() {
420 if !visited[*u] {
421 visited[*u] = true;
422 }
423 if *idx < self.adj[*u].len() {
424 let v = self.adj[*u][*idx];
425 *idx += 1;
426 if !visited[v] {
427 stk.push((v, 0));
428 }
429 } else {
430 order.push(*u);
431 stk.pop();
432 }
433 }
434 }
435 }
436 let mut comp = vec![usize::MAX; self.n];
437 let mut components: Vec<Vec<usize>> = Vec::new();
438 for &start in order.iter().rev() {
439 if comp[start] == usize::MAX {
440 let cid = components.len();
441 let mut component = Vec::new();
442 let mut stk = vec![start];
443 while let Some(u) = stk.pop() {
444 if comp[u] == usize::MAX {
445 comp[u] = cid;
446 component.push(u);
447 for &v in &self.rev[u] {
448 if comp[v] == usize::MAX {
449 stk.push(v);
450 }
451 }
452 }
453 }
454 components.push(component);
455 }
456 }
457 components
458 }
459 #[allow(dead_code)]
460 pub fn node_count(&self) -> usize {
461 self.n
462 }
463 #[allow(dead_code)]
464 pub fn edge_count(&self) -> usize {
465 self.edge_count
466 }
467}
468#[allow(dead_code)]
470#[derive(Debug, Clone, Default)]
471pub struct KotlinX2PassStats {
472 pub iterations: usize,
473 pub changed: bool,
474 pub nodes_visited: usize,
475 pub nodes_modified: usize,
476 pub time_ms: u64,
477 pub memory_bytes: usize,
478 pub errors: usize,
479}
480impl KotlinX2PassStats {
481 #[allow(dead_code)]
482 pub fn new() -> Self {
483 Self::default()
484 }
485 #[allow(dead_code)]
486 pub fn visit(&mut self) {
487 self.nodes_visited += 1;
488 }
489 #[allow(dead_code)]
490 pub fn modify(&mut self) {
491 self.nodes_modified += 1;
492 self.changed = true;
493 }
494 #[allow(dead_code)]
495 pub fn iterate(&mut self) {
496 self.iterations += 1;
497 }
498 #[allow(dead_code)]
499 pub fn error(&mut self) {
500 self.errors += 1;
501 }
502 #[allow(dead_code)]
503 pub fn efficiency(&self) -> f64 {
504 if self.nodes_visited == 0 {
505 0.0
506 } else {
507 self.nodes_modified as f64 / self.nodes_visited as f64
508 }
509 }
510 #[allow(dead_code)]
511 pub fn merge(&mut self, o: &KotlinX2PassStats) {
512 self.iterations += o.iterations;
513 self.changed |= o.changed;
514 self.nodes_visited += o.nodes_visited;
515 self.nodes_modified += o.nodes_modified;
516 self.time_ms += o.time_ms;
517 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
518 self.errors += o.errors;
519 }
520}
521#[derive(Debug, Clone, PartialEq)]
523pub enum KotlinStmt {
524 Val(String, KotlinType, KotlinExpr),
526 Var(String, KotlinType, KotlinExpr),
528 Assign(String, KotlinExpr),
530 Return(KotlinExpr),
532 Expr(KotlinExpr),
534 If(KotlinExpr, Vec<KotlinStmt>, Vec<KotlinStmt>),
536 When(
538 KotlinExpr,
539 Vec<(KotlinExpr, Vec<KotlinStmt>)>,
540 Vec<KotlinStmt>,
541 ),
542}
543#[allow(dead_code)]
545#[derive(Debug, Clone, Default)]
546pub struct KotlinExtConstFolder {
547 pub(super) folds: usize,
548 pub(super) failures: usize,
549 pub(super) enabled: bool,
550}
551impl KotlinExtConstFolder {
552 #[allow(dead_code)]
553 pub fn new() -> Self {
554 Self {
555 folds: 0,
556 failures: 0,
557 enabled: true,
558 }
559 }
560 #[allow(dead_code)]
561 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
562 self.folds += 1;
563 a.checked_add(b)
564 }
565 #[allow(dead_code)]
566 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
567 self.folds += 1;
568 a.checked_sub(b)
569 }
570 #[allow(dead_code)]
571 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
572 self.folds += 1;
573 a.checked_mul(b)
574 }
575 #[allow(dead_code)]
576 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
577 if b == 0 {
578 self.failures += 1;
579 None
580 } else {
581 self.folds += 1;
582 a.checked_div(b)
583 }
584 }
585 #[allow(dead_code)]
586 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
587 if b == 0 {
588 self.failures += 1;
589 None
590 } else {
591 self.folds += 1;
592 a.checked_rem(b)
593 }
594 }
595 #[allow(dead_code)]
596 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
597 self.folds += 1;
598 a.checked_neg()
599 }
600 #[allow(dead_code)]
601 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
602 if s >= 64 {
603 self.failures += 1;
604 None
605 } else {
606 self.folds += 1;
607 a.checked_shl(s)
608 }
609 }
610 #[allow(dead_code)]
611 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
612 if s >= 64 {
613 self.failures += 1;
614 None
615 } else {
616 self.folds += 1;
617 a.checked_shr(s)
618 }
619 }
620 #[allow(dead_code)]
621 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
622 self.folds += 1;
623 a & b
624 }
625 #[allow(dead_code)]
626 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
627 self.folds += 1;
628 a | b
629 }
630 #[allow(dead_code)]
631 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
632 self.folds += 1;
633 a ^ b
634 }
635 #[allow(dead_code)]
636 pub fn not_i64(&mut self, a: i64) -> i64 {
637 self.folds += 1;
638 !a
639 }
640 #[allow(dead_code)]
641 pub fn fold_count(&self) -> usize {
642 self.folds
643 }
644 #[allow(dead_code)]
645 pub fn failure_count(&self) -> usize {
646 self.failures
647 }
648 #[allow(dead_code)]
649 pub fn enable(&mut self) {
650 self.enabled = true;
651 }
652 #[allow(dead_code)]
653 pub fn disable(&mut self) {
654 self.enabled = false;
655 }
656 #[allow(dead_code)]
657 pub fn is_enabled(&self) -> bool {
658 self.enabled
659 }
660}
661#[allow(dead_code)]
663#[derive(Debug, Clone)]
664pub struct KotlinX2Worklist {
665 pub(super) items: std::collections::VecDeque<usize>,
666 pub(super) present: Vec<bool>,
667}
668impl KotlinX2Worklist {
669 #[allow(dead_code)]
670 pub fn new(capacity: usize) -> Self {
671 Self {
672 items: std::collections::VecDeque::new(),
673 present: vec![false; capacity],
674 }
675 }
676 #[allow(dead_code)]
677 pub fn push(&mut self, id: usize) {
678 if id < self.present.len() && !self.present[id] {
679 self.present[id] = true;
680 self.items.push_back(id);
681 }
682 }
683 #[allow(dead_code)]
684 pub fn push_front(&mut self, id: usize) {
685 if id < self.present.len() && !self.present[id] {
686 self.present[id] = true;
687 self.items.push_front(id);
688 }
689 }
690 #[allow(dead_code)]
691 pub fn pop(&mut self) -> Option<usize> {
692 let id = self.items.pop_front()?;
693 if id < self.present.len() {
694 self.present[id] = false;
695 }
696 Some(id)
697 }
698 #[allow(dead_code)]
699 pub fn is_empty(&self) -> bool {
700 self.items.is_empty()
701 }
702 #[allow(dead_code)]
703 pub fn len(&self) -> usize {
704 self.items.len()
705 }
706 #[allow(dead_code)]
707 pub fn contains(&self, id: usize) -> bool {
708 id < self.present.len() && self.present[id]
709 }
710 #[allow(dead_code)]
711 pub fn drain_all(&mut self) -> Vec<usize> {
712 let v: Vec<usize> = self.items.drain(..).collect();
713 for &id in &v {
714 if id < self.present.len() {
715 self.present[id] = false;
716 }
717 }
718 v
719 }
720}
721#[allow(dead_code)]
722#[derive(Debug, Clone, Default)]
723pub struct KtPassStats {
724 pub total_runs: u32,
725 pub successful_runs: u32,
726 pub total_changes: u64,
727 pub time_ms: u64,
728 pub iterations_used: u32,
729}
730impl KtPassStats {
731 #[allow(dead_code)]
732 pub fn new() -> Self {
733 Self::default()
734 }
735 #[allow(dead_code)]
736 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
737 self.total_runs += 1;
738 self.successful_runs += 1;
739 self.total_changes += changes;
740 self.time_ms += time_ms;
741 self.iterations_used = iterations;
742 }
743 #[allow(dead_code)]
744 pub fn average_changes_per_run(&self) -> f64 {
745 if self.total_runs == 0 {
746 return 0.0;
747 }
748 self.total_changes as f64 / self.total_runs as f64
749 }
750 #[allow(dead_code)]
751 pub fn success_rate(&self) -> f64 {
752 if self.total_runs == 0 {
753 return 0.0;
754 }
755 self.successful_runs as f64 / self.total_runs as f64
756 }
757 #[allow(dead_code)]
758 pub fn format_summary(&self) -> String {
759 format!(
760 "Runs: {}/{}, Changes: {}, Time: {}ms",
761 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
762 )
763 }
764}
765#[allow(dead_code)]
766#[derive(Debug, Clone)]
767pub struct KtLivenessInfo {
768 pub live_in: Vec<std::collections::HashSet<u32>>,
769 pub live_out: Vec<std::collections::HashSet<u32>>,
770 pub defs: Vec<std::collections::HashSet<u32>>,
771 pub uses: Vec<std::collections::HashSet<u32>>,
772}
773impl KtLivenessInfo {
774 #[allow(dead_code)]
775 pub fn new(block_count: usize) -> Self {
776 KtLivenessInfo {
777 live_in: vec![std::collections::HashSet::new(); block_count],
778 live_out: vec![std::collections::HashSet::new(); block_count],
779 defs: vec![std::collections::HashSet::new(); block_count],
780 uses: vec![std::collections::HashSet::new(); block_count],
781 }
782 }
783 #[allow(dead_code)]
784 pub fn add_def(&mut self, block: usize, var: u32) {
785 if block < self.defs.len() {
786 self.defs[block].insert(var);
787 }
788 }
789 #[allow(dead_code)]
790 pub fn add_use(&mut self, block: usize, var: u32) {
791 if block < self.uses.len() {
792 self.uses[block].insert(var);
793 }
794 }
795 #[allow(dead_code)]
796 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
797 self.live_in
798 .get(block)
799 .map(|s| s.contains(&var))
800 .unwrap_or(false)
801 }
802 #[allow(dead_code)]
803 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
804 self.live_out
805 .get(block)
806 .map(|s| s.contains(&var))
807 .unwrap_or(false)
808 }
809}
810#[allow(dead_code)]
812#[derive(Debug, Clone, Default)]
813pub struct KotlinExtPassStats {
814 pub iterations: usize,
815 pub changed: bool,
816 pub nodes_visited: usize,
817 pub nodes_modified: usize,
818 pub time_ms: u64,
819 pub memory_bytes: usize,
820 pub errors: usize,
821}
822impl KotlinExtPassStats {
823 #[allow(dead_code)]
824 pub fn new() -> Self {
825 Self::default()
826 }
827 #[allow(dead_code)]
828 pub fn visit(&mut self) {
829 self.nodes_visited += 1;
830 }
831 #[allow(dead_code)]
832 pub fn modify(&mut self) {
833 self.nodes_modified += 1;
834 self.changed = true;
835 }
836 #[allow(dead_code)]
837 pub fn iterate(&mut self) {
838 self.iterations += 1;
839 }
840 #[allow(dead_code)]
841 pub fn error(&mut self) {
842 self.errors += 1;
843 }
844 #[allow(dead_code)]
845 pub fn efficiency(&self) -> f64 {
846 if self.nodes_visited == 0 {
847 0.0
848 } else {
849 self.nodes_modified as f64 / self.nodes_visited as f64
850 }
851 }
852 #[allow(dead_code)]
853 pub fn merge(&mut self, o: &KotlinExtPassStats) {
854 self.iterations += o.iterations;
855 self.changed |= o.changed;
856 self.nodes_visited += o.nodes_visited;
857 self.nodes_modified += o.nodes_modified;
858 self.time_ms += o.time_ms;
859 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
860 self.errors += o.errors;
861 }
862}
863#[allow(dead_code)]
865#[derive(Debug, Clone, Default)]
866pub struct KotlinExtLiveness {
867 pub live_in: Vec<Vec<usize>>,
868 pub live_out: Vec<Vec<usize>>,
869 pub defs: Vec<Vec<usize>>,
870 pub uses: Vec<Vec<usize>>,
871}
872impl KotlinExtLiveness {
873 #[allow(dead_code)]
874 pub fn new(n: usize) -> Self {
875 Self {
876 live_in: vec![Vec::new(); n],
877 live_out: vec![Vec::new(); n],
878 defs: vec![Vec::new(); n],
879 uses: vec![Vec::new(); n],
880 }
881 }
882 #[allow(dead_code)]
883 pub fn live_in(&self, b: usize, v: usize) -> bool {
884 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
885 }
886 #[allow(dead_code)]
887 pub fn live_out(&self, b: usize, v: usize) -> bool {
888 self.live_out
889 .get(b)
890 .map(|s| s.contains(&v))
891 .unwrap_or(false)
892 }
893 #[allow(dead_code)]
894 pub fn add_def(&mut self, b: usize, v: usize) {
895 if let Some(s) = self.defs.get_mut(b) {
896 if !s.contains(&v) {
897 s.push(v);
898 }
899 }
900 }
901 #[allow(dead_code)]
902 pub fn add_use(&mut self, b: usize, v: usize) {
903 if let Some(s) = self.uses.get_mut(b) {
904 if !s.contains(&v) {
905 s.push(v);
906 }
907 }
908 }
909 #[allow(dead_code)]
910 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
911 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
912 }
913 #[allow(dead_code)]
914 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
915 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
916 }
917}
918#[allow(dead_code)]
919#[derive(Debug, Clone)]
920pub struct KtPassConfig {
921 pub phase: KtPassPhase,
922 pub enabled: bool,
923 pub max_iterations: u32,
924 pub debug_output: bool,
925 pub pass_name: String,
926}
927impl KtPassConfig {
928 #[allow(dead_code)]
929 pub fn new(name: impl Into<String>, phase: KtPassPhase) -> Self {
930 KtPassConfig {
931 phase,
932 enabled: true,
933 max_iterations: 10,
934 debug_output: false,
935 pass_name: name.into(),
936 }
937 }
938 #[allow(dead_code)]
939 pub fn disabled(mut self) -> Self {
940 self.enabled = false;
941 self
942 }
943 #[allow(dead_code)]
944 pub fn with_debug(mut self) -> Self {
945 self.debug_output = true;
946 self
947 }
948 #[allow(dead_code)]
949 pub fn max_iter(mut self, n: u32) -> Self {
950 self.max_iterations = n;
951 self
952 }
953}
954#[allow(dead_code)]
956#[derive(Debug, Clone, PartialEq, Eq, Hash)]
957pub enum KotlinExtPassPhase {
958 Early,
959 Middle,
960 Late,
961 Finalize,
962}
963impl KotlinExtPassPhase {
964 #[allow(dead_code)]
965 pub fn is_early(&self) -> bool {
966 matches!(self, Self::Early)
967 }
968 #[allow(dead_code)]
969 pub fn is_middle(&self) -> bool {
970 matches!(self, Self::Middle)
971 }
972 #[allow(dead_code)]
973 pub fn is_late(&self) -> bool {
974 matches!(self, Self::Late)
975 }
976 #[allow(dead_code)]
977 pub fn is_finalize(&self) -> bool {
978 matches!(self, Self::Finalize)
979 }
980 #[allow(dead_code)]
981 pub fn order(&self) -> u32 {
982 match self {
983 Self::Early => 0,
984 Self::Middle => 1,
985 Self::Late => 2,
986 Self::Finalize => 3,
987 }
988 }
989 #[allow(dead_code)]
990 pub fn from_order(n: u32) -> Option<Self> {
991 match n {
992 0 => Some(Self::Early),
993 1 => Some(Self::Middle),
994 2 => Some(Self::Late),
995 3 => Some(Self::Finalize),
996 _ => None,
997 }
998 }
999}
1000#[derive(Debug, Clone, PartialEq)]
1002pub enum KotlinExpr {
1003 Var(String),
1005 Lit(KotlinLit),
1007 Call(Box<KotlinExpr>, Vec<KotlinExpr>),
1009 BinOp(String, Box<KotlinExpr>, Box<KotlinExpr>),
1011 Member(Box<KotlinExpr>, String),
1013 Index(Box<KotlinExpr>, Box<KotlinExpr>),
1015 Unary(String, Box<KotlinExpr>),
1017 Lambda(Vec<String>, Box<KotlinExpr>),
1019 When(
1021 Box<KotlinExpr>,
1022 Vec<KotlinWhenBranch>,
1023 Option<Box<KotlinExpr>>,
1024 ),
1025 Elvis(Box<KotlinExpr>, Box<KotlinExpr>),
1027}
1028#[derive(Debug, Clone)]
1030pub struct KotlinDataClass {
1031 pub name: String,
1032 pub fields: Vec<(String, KotlinType)>,
1033}
1034#[allow(dead_code)]
1036#[derive(Debug, Clone)]
1037pub struct KotlinExtWorklist {
1038 pub(super) items: std::collections::VecDeque<usize>,
1039 pub(super) present: Vec<bool>,
1040}
1041impl KotlinExtWorklist {
1042 #[allow(dead_code)]
1043 pub fn new(capacity: usize) -> Self {
1044 Self {
1045 items: std::collections::VecDeque::new(),
1046 present: vec![false; capacity],
1047 }
1048 }
1049 #[allow(dead_code)]
1050 pub fn push(&mut self, id: usize) {
1051 if id < self.present.len() && !self.present[id] {
1052 self.present[id] = true;
1053 self.items.push_back(id);
1054 }
1055 }
1056 #[allow(dead_code)]
1057 pub fn push_front(&mut self, id: usize) {
1058 if id < self.present.len() && !self.present[id] {
1059 self.present[id] = true;
1060 self.items.push_front(id);
1061 }
1062 }
1063 #[allow(dead_code)]
1064 pub fn pop(&mut self) -> Option<usize> {
1065 let id = self.items.pop_front()?;
1066 if id < self.present.len() {
1067 self.present[id] = false;
1068 }
1069 Some(id)
1070 }
1071 #[allow(dead_code)]
1072 pub fn is_empty(&self) -> bool {
1073 self.items.is_empty()
1074 }
1075 #[allow(dead_code)]
1076 pub fn len(&self) -> usize {
1077 self.items.len()
1078 }
1079 #[allow(dead_code)]
1080 pub fn contains(&self, id: usize) -> bool {
1081 id < self.present.len() && self.present[id]
1082 }
1083 #[allow(dead_code)]
1084 pub fn drain_all(&mut self) -> Vec<usize> {
1085 let v: Vec<usize> = self.items.drain(..).collect();
1086 for &id in &v {
1087 if id < self.present.len() {
1088 self.present[id] = false;
1089 }
1090 }
1091 v
1092 }
1093}
1094#[derive(Debug, Clone)]
1096pub struct KotlinFunc {
1097 pub name: String,
1098 pub params: Vec<(String, KotlinType)>,
1099 pub return_type: KotlinType,
1100 pub body: Vec<KotlinStmt>,
1101 pub is_tailrec: bool,
1102}
1103#[derive(Debug, Clone, PartialEq)]
1105pub enum KotlinLit {
1106 Int(i64),
1107 Long(i64),
1108 Bool(bool),
1109 Str(String),
1110 Null,
1111}
1112#[allow(dead_code)]
1114#[derive(Debug)]
1115pub struct KotlinX2Cache {
1116 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1117 pub(super) cap: usize,
1118 pub(super) total_hits: u64,
1119 pub(super) total_misses: u64,
1120}
1121impl KotlinX2Cache {
1122 #[allow(dead_code)]
1123 pub fn new(cap: usize) -> Self {
1124 Self {
1125 entries: Vec::new(),
1126 cap,
1127 total_hits: 0,
1128 total_misses: 0,
1129 }
1130 }
1131 #[allow(dead_code)]
1132 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1133 for e in self.entries.iter_mut() {
1134 if e.0 == key && e.2 {
1135 e.3 += 1;
1136 self.total_hits += 1;
1137 return Some(&e.1);
1138 }
1139 }
1140 self.total_misses += 1;
1141 None
1142 }
1143 #[allow(dead_code)]
1144 pub fn put(&mut self, key: u64, data: Vec<u8>) {
1145 if self.entries.len() >= self.cap {
1146 self.entries.retain(|e| e.2);
1147 if self.entries.len() >= self.cap {
1148 self.entries.remove(0);
1149 }
1150 }
1151 self.entries.push((key, data, true, 0));
1152 }
1153 #[allow(dead_code)]
1154 pub fn invalidate(&mut self) {
1155 for e in self.entries.iter_mut() {
1156 e.2 = false;
1157 }
1158 }
1159 #[allow(dead_code)]
1160 pub fn hit_rate(&self) -> f64 {
1161 let t = self.total_hits + self.total_misses;
1162 if t == 0 {
1163 0.0
1164 } else {
1165 self.total_hits as f64 / t as f64
1166 }
1167 }
1168 #[allow(dead_code)]
1169 pub fn live_count(&self) -> usize {
1170 self.entries.iter().filter(|e| e.2).count()
1171 }
1172}
1173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1175pub enum KotlinType {
1176 KtInt,
1178 KtLong,
1180 KtBool,
1182 KtString,
1184 KtUnit,
1186 KtAny,
1188 KtList(Box<KotlinType>),
1190 KtPair(Box<KotlinType>, Box<KotlinType>),
1192 KtFunc(Vec<KotlinType>, Box<KotlinType>),
1194 KtNullable(Box<KotlinType>),
1196 KtObject(String),
1198}
1199pub struct KotlinBackend {
1201 pub(super) var_counter: u64,
1203}
1204impl KotlinBackend {
1205 pub fn new() -> Self {
1207 KotlinBackend { var_counter: 0 }
1208 }
1209 pub fn mangle_name(&self, name: &str) -> String {
1211 let sanitized: String = name
1212 .chars()
1213 .map(|c| match c {
1214 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => c,
1215 '.' | ':' | '\'' | '!' | '?' => '_',
1216 _ => '_',
1217 })
1218 .collect();
1219 let sanitized = if sanitized.starts_with(|c: char| c.is_ascii_digit()) {
1220 format!("_{}", sanitized)
1221 } else {
1222 sanitized
1223 };
1224 if KOTLIN_KEYWORDS.contains(&sanitized.as_str()) {
1225 format!("{}_", sanitized)
1226 } else if sanitized.is_empty() {
1227 "_anon".to_string()
1228 } else {
1229 sanitized
1230 }
1231 }
1232 pub fn fresh_var(&mut self) -> String {
1234 let v = self.var_counter;
1235 self.var_counter += 1;
1236 format!("_t{}", v)
1237 }
1238 pub fn compile_module(decls: &[LcnfFunDecl]) -> Result<String, String> {
1240 let mut backend = KotlinBackend::new();
1241 let mut funs = Vec::new();
1242 let mut ctor_names: HashSet<String> = HashSet::new();
1243 for decl in decls {
1244 collect_ctor_names_from_expr(&decl.body, &mut ctor_names);
1245 }
1246 for decl in decls {
1247 let f = backend.compile_decl(decl)?;
1248 funs.push(f);
1249 }
1250 let data_classes: Vec<KotlinDataClass> = ctor_names
1251 .into_iter()
1252 .collect::<Vec<_>>()
1253 .into_iter()
1254 .map(|name| KotlinDataClass {
1255 fields: vec![("tag".to_string(), KotlinType::KtInt)],
1256 name,
1257 })
1258 .collect();
1259 let module = KotlinModule {
1260 package: "oxilean.generated".to_string(),
1261 imports: vec![],
1262 data_classes,
1263 funs,
1264 };
1265 Ok(module.to_string())
1266 }
1267 pub fn compile_decl(&mut self, decl: &LcnfFunDecl) -> Result<KotlinFunc, String> {
1269 let name = self.mangle_name(&decl.name.to_string());
1270 let params: Vec<(String, KotlinType)> = decl
1271 .params
1272 .iter()
1273 .map(|p| (self.mangle_name(&p.name), lcnf_type_to_kotlin(&p.ty)))
1274 .collect();
1275 let return_type = lcnf_type_to_kotlin(&decl.ret_type);
1276 let mut stmts: Vec<KotlinStmt> = Vec::new();
1277 let result_expr = self.compile_expr(&decl.body, &mut stmts)?;
1278 stmts.push(KotlinStmt::Return(result_expr));
1279 Ok(KotlinFunc {
1280 name,
1281 params,
1282 return_type,
1283 body: stmts,
1284 is_tailrec: false,
1285 })
1286 }
1287 pub fn compile_expr(
1290 &mut self,
1291 expr: &LcnfExpr,
1292 stmts: &mut Vec<KotlinStmt>,
1293 ) -> Result<KotlinExpr, String> {
1294 match expr {
1295 LcnfExpr::Return(arg) => Ok(self.compile_arg(arg)),
1296 LcnfExpr::Unreachable => Ok(KotlinExpr::Call(
1297 Box::new(KotlinExpr::Member(
1298 Box::new(KotlinExpr::Var("OxiLeanRuntime".to_string())),
1299 "unreachable".to_string(),
1300 )),
1301 vec![],
1302 )),
1303 LcnfExpr::TailCall(func, args) => {
1304 let callee = self.compile_arg(func);
1305 let kt_args: Vec<KotlinExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1306 Ok(KotlinExpr::Call(Box::new(callee), kt_args))
1307 }
1308 LcnfExpr::Let {
1309 id: _,
1310 name,
1311 ty,
1312 value,
1313 body,
1314 } => {
1315 let kt_val = self.compile_let_value(value)?;
1316 let var_name = self.mangle_name(name);
1317 let kt_ty = lcnf_type_to_kotlin(ty);
1318 stmts.push(KotlinStmt::Val(var_name.clone(), kt_ty, kt_val));
1319 self.compile_expr(body, stmts)
1320 }
1321 LcnfExpr::Case {
1322 scrutinee,
1323 scrutinee_ty: _,
1324 alts,
1325 default,
1326 } => {
1327 let scrutinee_expr = KotlinExpr::Var(format!("_x{}", scrutinee.0));
1328 let result_var = self.fresh_var();
1329 let mut branches: Vec<(KotlinExpr, Vec<KotlinStmt>)> = Vec::new();
1330 for alt in alts {
1331 let mut branch_stmts: Vec<KotlinStmt> = Vec::new();
1332 for (idx, param) in alt.params.iter().enumerate() {
1333 let param_name = self.mangle_name(¶m.name);
1334 let field_access = KotlinExpr::Member(
1335 Box::new(KotlinExpr::Var(format!("_x{}", scrutinee.0))),
1336 format!("field{}", idx),
1337 );
1338 branch_stmts.push(KotlinStmt::Val(
1339 param_name,
1340 lcnf_type_to_kotlin(¶m.ty),
1341 field_access,
1342 ));
1343 }
1344 let branch_result = self.compile_expr(&alt.body, &mut branch_stmts)?;
1345 branch_stmts.push(KotlinStmt::Assign(result_var.clone(), branch_result));
1346 let tag_cond = KotlinExpr::Lit(KotlinLit::Int(alt.ctor_tag as i64));
1347 branches.push((tag_cond, branch_stmts));
1348 }
1349 let mut default_stmts: Vec<KotlinStmt> = Vec::new();
1350 if let Some(def) = default {
1351 let def_result = self.compile_expr(def, &mut default_stmts)?;
1352 default_stmts.push(KotlinStmt::Assign(result_var.clone(), def_result));
1353 } else {
1354 default_stmts.push(KotlinStmt::Expr(KotlinExpr::Call(
1355 Box::new(KotlinExpr::Member(
1356 Box::new(KotlinExpr::Var("OxiLeanRuntime".to_string())),
1357 "unreachable".to_string(),
1358 )),
1359 vec![],
1360 )));
1361 }
1362 let discriminant = KotlinExpr::Member(Box::new(scrutinee_expr), "tag".to_string());
1363 stmts.push(KotlinStmt::Var(
1364 result_var.clone(),
1365 KotlinType::KtAny,
1366 KotlinExpr::Lit(KotlinLit::Null),
1367 ));
1368 stmts.push(KotlinStmt::When(discriminant, branches, default_stmts));
1369 Ok(KotlinExpr::Var(result_var))
1370 }
1371 }
1372 }
1373 pub(super) fn compile_let_value(&mut self, value: &LcnfLetValue) -> Result<KotlinExpr, String> {
1375 match value {
1376 LcnfLetValue::Lit(lit) => Ok(self.compile_lit(lit)),
1377 LcnfLetValue::Erased => Ok(KotlinExpr::Lit(KotlinLit::Null)),
1378 LcnfLetValue::FVar(id) => Ok(KotlinExpr::Var(format!("_x{}", id.0))),
1379 LcnfLetValue::App(func, args) => {
1380 let callee = self.compile_arg(func);
1381 let kt_args: Vec<KotlinExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1382 Ok(KotlinExpr::Call(Box::new(callee), kt_args))
1383 }
1384 LcnfLetValue::Proj(_name, idx, var) => {
1385 let base = KotlinExpr::Var(format!("_x{}", var.0));
1386 Ok(KotlinExpr::Member(Box::new(base), format!("field{}", idx)))
1387 }
1388 LcnfLetValue::Ctor(name, _tag, args) => {
1389 let ctor_name = self.mangle_name(name);
1390 let kt_args: Vec<KotlinExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1391 Ok(KotlinExpr::Call(
1392 Box::new(KotlinExpr::Var(ctor_name)),
1393 kt_args,
1394 ))
1395 }
1396 LcnfLetValue::Reset(_var) => Ok(KotlinExpr::Lit(KotlinLit::Null)),
1397 LcnfLetValue::Reuse(_slot, name, _tag, args) => {
1398 let ctor_name = self.mangle_name(name);
1399 let kt_args: Vec<KotlinExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1400 Ok(KotlinExpr::Call(
1401 Box::new(KotlinExpr::Var(ctor_name)),
1402 kt_args,
1403 ))
1404 }
1405 }
1406 }
1407 pub(super) fn compile_arg(&self, arg: &LcnfArg) -> KotlinExpr {
1409 match arg {
1410 LcnfArg::Var(id) => KotlinExpr::Var(format!("_x{}", id.0)),
1411 LcnfArg::Lit(lit) => self.compile_lit_ref(lit),
1412 LcnfArg::Erased => KotlinExpr::Lit(KotlinLit::Null),
1413 LcnfArg::Type(_) => KotlinExpr::Lit(KotlinLit::Null),
1414 }
1415 }
1416 pub(super) fn compile_lit(&self, lit: &LcnfLit) -> KotlinExpr {
1418 match lit {
1419 LcnfLit::Nat(n) => KotlinExpr::Lit(KotlinLit::Long(*n as i64)),
1420 LcnfLit::Str(s) => KotlinExpr::Lit(KotlinLit::Str(s.clone())),
1421 }
1422 }
1423 pub(super) fn compile_lit_ref(&self, lit: &LcnfLit) -> KotlinExpr {
1425 self.compile_lit(lit)
1426 }
1427}
1428#[allow(dead_code)]
1429#[derive(Debug, Clone)]
1430pub struct KtDominatorTree {
1431 pub idom: Vec<Option<u32>>,
1432 pub dom_children: Vec<Vec<u32>>,
1433 pub dom_depth: Vec<u32>,
1434}
1435impl KtDominatorTree {
1436 #[allow(dead_code)]
1437 pub fn new(size: usize) -> Self {
1438 KtDominatorTree {
1439 idom: vec![None; size],
1440 dom_children: vec![Vec::new(); size],
1441 dom_depth: vec![0; size],
1442 }
1443 }
1444 #[allow(dead_code)]
1445 pub fn set_idom(&mut self, node: usize, idom: u32) {
1446 self.idom[node] = Some(idom);
1447 }
1448 #[allow(dead_code)]
1449 pub fn dominates(&self, a: usize, b: usize) -> bool {
1450 if a == b {
1451 return true;
1452 }
1453 let mut cur = b;
1454 loop {
1455 match self.idom[cur] {
1456 Some(parent) if parent as usize == a => return true,
1457 Some(parent) if parent as usize == cur => return false,
1458 Some(parent) => cur = parent as usize,
1459 None => return false,
1460 }
1461 }
1462 }
1463 #[allow(dead_code)]
1464 pub fn depth(&self, node: usize) -> u32 {
1465 self.dom_depth.get(node).copied().unwrap_or(0)
1466 }
1467}
1468#[allow(dead_code)]
1470#[derive(Debug, Default)]
1471pub struct KotlinX2PassRegistry {
1472 pub(super) configs: Vec<KotlinX2PassConfig>,
1473 pub(super) stats: Vec<KotlinX2PassStats>,
1474}
1475impl KotlinX2PassRegistry {
1476 #[allow(dead_code)]
1477 pub fn new() -> Self {
1478 Self::default()
1479 }
1480 #[allow(dead_code)]
1481 pub fn register(&mut self, c: KotlinX2PassConfig) {
1482 self.stats.push(KotlinX2PassStats::new());
1483 self.configs.push(c);
1484 }
1485 #[allow(dead_code)]
1486 pub fn len(&self) -> usize {
1487 self.configs.len()
1488 }
1489 #[allow(dead_code)]
1490 pub fn is_empty(&self) -> bool {
1491 self.configs.is_empty()
1492 }
1493 #[allow(dead_code)]
1494 pub fn get(&self, i: usize) -> Option<&KotlinX2PassConfig> {
1495 self.configs.get(i)
1496 }
1497 #[allow(dead_code)]
1498 pub fn get_stats(&self, i: usize) -> Option<&KotlinX2PassStats> {
1499 self.stats.get(i)
1500 }
1501 #[allow(dead_code)]
1502 pub fn enabled_passes(&self) -> Vec<&KotlinX2PassConfig> {
1503 self.configs.iter().filter(|c| c.enabled).collect()
1504 }
1505 #[allow(dead_code)]
1506 pub fn passes_in_phase(&self, ph: &KotlinX2PassPhase) -> Vec<&KotlinX2PassConfig> {
1507 self.configs
1508 .iter()
1509 .filter(|c| c.enabled && &c.phase == ph)
1510 .collect()
1511 }
1512 #[allow(dead_code)]
1513 pub fn total_nodes_visited(&self) -> usize {
1514 self.stats.iter().map(|s| s.nodes_visited).sum()
1515 }
1516 #[allow(dead_code)]
1517 pub fn any_changed(&self) -> bool {
1518 self.stats.iter().any(|s| s.changed)
1519 }
1520}
1521#[allow(dead_code)]
1523#[derive(Debug, Clone)]
1524pub struct KotlinX2PassConfig {
1525 pub name: String,
1526 pub phase: KotlinX2PassPhase,
1527 pub enabled: bool,
1528 pub max_iterations: usize,
1529 pub debug: u32,
1530 pub timeout_ms: Option<u64>,
1531}
1532impl KotlinX2PassConfig {
1533 #[allow(dead_code)]
1534 pub fn new(name: impl Into<String>) -> Self {
1535 Self {
1536 name: name.into(),
1537 phase: KotlinX2PassPhase::Middle,
1538 enabled: true,
1539 max_iterations: 100,
1540 debug: 0,
1541 timeout_ms: None,
1542 }
1543 }
1544 #[allow(dead_code)]
1545 pub fn with_phase(mut self, phase: KotlinX2PassPhase) -> Self {
1546 self.phase = phase;
1547 self
1548 }
1549 #[allow(dead_code)]
1550 pub fn with_max_iter(mut self, n: usize) -> Self {
1551 self.max_iterations = n;
1552 self
1553 }
1554 #[allow(dead_code)]
1555 pub fn with_debug(mut self, d: u32) -> Self {
1556 self.debug = d;
1557 self
1558 }
1559 #[allow(dead_code)]
1560 pub fn disabled(mut self) -> Self {
1561 self.enabled = false;
1562 self
1563 }
1564 #[allow(dead_code)]
1565 pub fn with_timeout(mut self, ms: u64) -> Self {
1566 self.timeout_ms = Some(ms);
1567 self
1568 }
1569 #[allow(dead_code)]
1570 pub fn is_debug_enabled(&self) -> bool {
1571 self.debug > 0
1572 }
1573}
1574#[allow(dead_code)]
1576#[derive(Debug, Clone)]
1577pub struct KotlinX2DepGraph {
1578 pub(super) n: usize,
1579 pub(super) adj: Vec<Vec<usize>>,
1580 pub(super) rev: Vec<Vec<usize>>,
1581 pub(super) edge_count: usize,
1582}
1583impl KotlinX2DepGraph {
1584 #[allow(dead_code)]
1585 pub fn new(n: usize) -> Self {
1586 Self {
1587 n,
1588 adj: vec![Vec::new(); n],
1589 rev: vec![Vec::new(); n],
1590 edge_count: 0,
1591 }
1592 }
1593 #[allow(dead_code)]
1594 pub fn add_edge(&mut self, from: usize, to: usize) {
1595 if from < self.n && to < self.n {
1596 if !self.adj[from].contains(&to) {
1597 self.adj[from].push(to);
1598 self.rev[to].push(from);
1599 self.edge_count += 1;
1600 }
1601 }
1602 }
1603 #[allow(dead_code)]
1604 pub fn succs(&self, n: usize) -> &[usize] {
1605 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1606 }
1607 #[allow(dead_code)]
1608 pub fn preds(&self, n: usize) -> &[usize] {
1609 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1610 }
1611 #[allow(dead_code)]
1612 pub fn topo_sort(&self) -> Option<Vec<usize>> {
1613 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
1614 let mut q: std::collections::VecDeque<usize> =
1615 (0..self.n).filter(|&i| deg[i] == 0).collect();
1616 let mut out = Vec::with_capacity(self.n);
1617 while let Some(u) = q.pop_front() {
1618 out.push(u);
1619 for &v in &self.adj[u] {
1620 deg[v] -= 1;
1621 if deg[v] == 0 {
1622 q.push_back(v);
1623 }
1624 }
1625 }
1626 if out.len() == self.n {
1627 Some(out)
1628 } else {
1629 None
1630 }
1631 }
1632 #[allow(dead_code)]
1633 pub fn has_cycle(&self) -> bool {
1634 self.topo_sort().is_none()
1635 }
1636 #[allow(dead_code)]
1637 pub fn reachable(&self, start: usize) -> Vec<usize> {
1638 let mut vis = vec![false; self.n];
1639 let mut stk = vec![start];
1640 let mut out = Vec::new();
1641 while let Some(u) = stk.pop() {
1642 if u < self.n && !vis[u] {
1643 vis[u] = true;
1644 out.push(u);
1645 for &v in &self.adj[u] {
1646 if !vis[v] {
1647 stk.push(v);
1648 }
1649 }
1650 }
1651 }
1652 out
1653 }
1654 #[allow(dead_code)]
1655 pub fn scc(&self) -> Vec<Vec<usize>> {
1656 let mut visited = vec![false; self.n];
1657 let mut order = Vec::new();
1658 for i in 0..self.n {
1659 if !visited[i] {
1660 let mut stk = vec![(i, 0usize)];
1661 while let Some((u, idx)) = stk.last_mut() {
1662 if !visited[*u] {
1663 visited[*u] = true;
1664 }
1665 if *idx < self.adj[*u].len() {
1666 let v = self.adj[*u][*idx];
1667 *idx += 1;
1668 if !visited[v] {
1669 stk.push((v, 0));
1670 }
1671 } else {
1672 order.push(*u);
1673 stk.pop();
1674 }
1675 }
1676 }
1677 }
1678 let mut comp = vec![usize::MAX; self.n];
1679 let mut components: Vec<Vec<usize>> = Vec::new();
1680 for &start in order.iter().rev() {
1681 if comp[start] == usize::MAX {
1682 let cid = components.len();
1683 let mut component = Vec::new();
1684 let mut stk = vec![start];
1685 while let Some(u) = stk.pop() {
1686 if comp[u] == usize::MAX {
1687 comp[u] = cid;
1688 component.push(u);
1689 for &v in &self.rev[u] {
1690 if comp[v] == usize::MAX {
1691 stk.push(v);
1692 }
1693 }
1694 }
1695 }
1696 components.push(component);
1697 }
1698 }
1699 components
1700 }
1701 #[allow(dead_code)]
1702 pub fn node_count(&self) -> usize {
1703 self.n
1704 }
1705 #[allow(dead_code)]
1706 pub fn edge_count(&self) -> usize {
1707 self.edge_count
1708 }
1709}
1710#[allow(dead_code)]
1712#[derive(Debug, Clone)]
1713pub struct KotlinX2DomTree {
1714 pub(super) idom: Vec<Option<usize>>,
1715 pub(super) children: Vec<Vec<usize>>,
1716 pub(super) depth: Vec<usize>,
1717}
1718impl KotlinX2DomTree {
1719 #[allow(dead_code)]
1720 pub fn new(n: usize) -> Self {
1721 Self {
1722 idom: vec![None; n],
1723 children: vec![Vec::new(); n],
1724 depth: vec![0; n],
1725 }
1726 }
1727 #[allow(dead_code)]
1728 pub fn set_idom(&mut self, node: usize, dom: usize) {
1729 if node < self.idom.len() {
1730 self.idom[node] = Some(dom);
1731 if dom < self.children.len() {
1732 self.children[dom].push(node);
1733 }
1734 self.depth[node] = if dom < self.depth.len() {
1735 self.depth[dom] + 1
1736 } else {
1737 1
1738 };
1739 }
1740 }
1741 #[allow(dead_code)]
1742 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1743 if a == b {
1744 return true;
1745 }
1746 let n = self.idom.len();
1747 for _ in 0..n {
1748 match self.idom.get(b).copied().flatten() {
1749 None => return false,
1750 Some(p) if p == a => return true,
1751 Some(p) if p == b => return false,
1752 Some(p) => b = p,
1753 }
1754 }
1755 false
1756 }
1757 #[allow(dead_code)]
1758 pub fn children_of(&self, n: usize) -> &[usize] {
1759 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1760 }
1761 #[allow(dead_code)]
1762 pub fn depth_of(&self, n: usize) -> usize {
1763 self.depth.get(n).copied().unwrap_or(0)
1764 }
1765 #[allow(dead_code)]
1766 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1767 let n = self.idom.len();
1768 for _ in 0..(2 * n) {
1769 if a == b {
1770 return a;
1771 }
1772 if self.depth_of(a) > self.depth_of(b) {
1773 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1774 } else {
1775 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1776 }
1777 }
1778 0
1779 }
1780}
1781#[allow(dead_code)]
1783#[derive(Debug, Clone, Default)]
1784pub struct KotlinX2Liveness {
1785 pub live_in: Vec<Vec<usize>>,
1786 pub live_out: Vec<Vec<usize>>,
1787 pub defs: Vec<Vec<usize>>,
1788 pub uses: Vec<Vec<usize>>,
1789}
1790impl KotlinX2Liveness {
1791 #[allow(dead_code)]
1792 pub fn new(n: usize) -> Self {
1793 Self {
1794 live_in: vec![Vec::new(); n],
1795 live_out: vec![Vec::new(); n],
1796 defs: vec![Vec::new(); n],
1797 uses: vec![Vec::new(); n],
1798 }
1799 }
1800 #[allow(dead_code)]
1801 pub fn live_in(&self, b: usize, v: usize) -> bool {
1802 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1803 }
1804 #[allow(dead_code)]
1805 pub fn live_out(&self, b: usize, v: usize) -> bool {
1806 self.live_out
1807 .get(b)
1808 .map(|s| s.contains(&v))
1809 .unwrap_or(false)
1810 }
1811 #[allow(dead_code)]
1812 pub fn add_def(&mut self, b: usize, v: usize) {
1813 if let Some(s) = self.defs.get_mut(b) {
1814 if !s.contains(&v) {
1815 s.push(v);
1816 }
1817 }
1818 }
1819 #[allow(dead_code)]
1820 pub fn add_use(&mut self, b: usize, v: usize) {
1821 if let Some(s) = self.uses.get_mut(b) {
1822 if !s.contains(&v) {
1823 s.push(v);
1824 }
1825 }
1826 }
1827 #[allow(dead_code)]
1828 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1829 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1830 }
1831 #[allow(dead_code)]
1832 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1833 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1834 }
1835}
1836#[allow(dead_code)]
1837#[derive(Debug, Clone, PartialEq)]
1838pub enum KtPassPhase {
1839 Analysis,
1840 Transformation,
1841 Verification,
1842 Cleanup,
1843}
1844impl KtPassPhase {
1845 #[allow(dead_code)]
1846 pub fn name(&self) -> &str {
1847 match self {
1848 KtPassPhase::Analysis => "analysis",
1849 KtPassPhase::Transformation => "transformation",
1850 KtPassPhase::Verification => "verification",
1851 KtPassPhase::Cleanup => "cleanup",
1852 }
1853 }
1854 #[allow(dead_code)]
1855 pub fn is_modifying(&self) -> bool {
1856 matches!(self, KtPassPhase::Transformation | KtPassPhase::Cleanup)
1857 }
1858}
1859#[allow(dead_code)]
1860#[derive(Debug, Clone)]
1861pub struct KtCacheEntry {
1862 pub key: String,
1863 pub data: Vec<u8>,
1864 pub timestamp: u64,
1865 pub valid: bool,
1866}
1867#[derive(Debug, Clone)]
1869pub struct KotlinModule {
1870 pub package: String,
1871 pub imports: Vec<String>,
1872 pub data_classes: Vec<KotlinDataClass>,
1873 pub funs: Vec<KotlinFunc>,
1874}
1875#[allow(dead_code)]
1877#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1878pub enum KotlinX2PassPhase {
1879 Early,
1880 Middle,
1881 Late,
1882 Finalize,
1883}
1884impl KotlinX2PassPhase {
1885 #[allow(dead_code)]
1886 pub fn is_early(&self) -> bool {
1887 matches!(self, Self::Early)
1888 }
1889 #[allow(dead_code)]
1890 pub fn is_middle(&self) -> bool {
1891 matches!(self, Self::Middle)
1892 }
1893 #[allow(dead_code)]
1894 pub fn is_late(&self) -> bool {
1895 matches!(self, Self::Late)
1896 }
1897 #[allow(dead_code)]
1898 pub fn is_finalize(&self) -> bool {
1899 matches!(self, Self::Finalize)
1900 }
1901 #[allow(dead_code)]
1902 pub fn order(&self) -> u32 {
1903 match self {
1904 Self::Early => 0,
1905 Self::Middle => 1,
1906 Self::Late => 2,
1907 Self::Finalize => 3,
1908 }
1909 }
1910 #[allow(dead_code)]
1911 pub fn from_order(n: u32) -> Option<Self> {
1912 match n {
1913 0 => Some(Self::Early),
1914 1 => Some(Self::Middle),
1915 2 => Some(Self::Late),
1916 3 => Some(Self::Finalize),
1917 _ => None,
1918 }
1919 }
1920}
1921#[allow(dead_code)]
1922#[derive(Debug, Clone)]
1923pub struct KtWorklist {
1924 pub(super) items: std::collections::VecDeque<u32>,
1925 pub(super) in_worklist: std::collections::HashSet<u32>,
1926}
1927impl KtWorklist {
1928 #[allow(dead_code)]
1929 pub fn new() -> Self {
1930 KtWorklist {
1931 items: std::collections::VecDeque::new(),
1932 in_worklist: std::collections::HashSet::new(),
1933 }
1934 }
1935 #[allow(dead_code)]
1936 pub fn push(&mut self, item: u32) -> bool {
1937 if self.in_worklist.insert(item) {
1938 self.items.push_back(item);
1939 true
1940 } else {
1941 false
1942 }
1943 }
1944 #[allow(dead_code)]
1945 pub fn pop(&mut self) -> Option<u32> {
1946 let item = self.items.pop_front()?;
1947 self.in_worklist.remove(&item);
1948 Some(item)
1949 }
1950 #[allow(dead_code)]
1951 pub fn is_empty(&self) -> bool {
1952 self.items.is_empty()
1953 }
1954 #[allow(dead_code)]
1955 pub fn len(&self) -> usize {
1956 self.items.len()
1957 }
1958 #[allow(dead_code)]
1959 pub fn contains(&self, item: u32) -> bool {
1960 self.in_worklist.contains(&item)
1961 }
1962}
1963#[allow(dead_code)]
1965#[derive(Debug, Clone, Default)]
1966pub struct KotlinX2ConstFolder {
1967 pub(super) folds: usize,
1968 pub(super) failures: usize,
1969 pub(super) enabled: bool,
1970}
1971impl KotlinX2ConstFolder {
1972 #[allow(dead_code)]
1973 pub fn new() -> Self {
1974 Self {
1975 folds: 0,
1976 failures: 0,
1977 enabled: true,
1978 }
1979 }
1980 #[allow(dead_code)]
1981 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1982 self.folds += 1;
1983 a.checked_add(b)
1984 }
1985 #[allow(dead_code)]
1986 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1987 self.folds += 1;
1988 a.checked_sub(b)
1989 }
1990 #[allow(dead_code)]
1991 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1992 self.folds += 1;
1993 a.checked_mul(b)
1994 }
1995 #[allow(dead_code)]
1996 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1997 if b == 0 {
1998 self.failures += 1;
1999 None
2000 } else {
2001 self.folds += 1;
2002 a.checked_div(b)
2003 }
2004 }
2005 #[allow(dead_code)]
2006 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
2007 if b == 0 {
2008 self.failures += 1;
2009 None
2010 } else {
2011 self.folds += 1;
2012 a.checked_rem(b)
2013 }
2014 }
2015 #[allow(dead_code)]
2016 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
2017 self.folds += 1;
2018 a.checked_neg()
2019 }
2020 #[allow(dead_code)]
2021 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
2022 if s >= 64 {
2023 self.failures += 1;
2024 None
2025 } else {
2026 self.folds += 1;
2027 a.checked_shl(s)
2028 }
2029 }
2030 #[allow(dead_code)]
2031 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
2032 if s >= 64 {
2033 self.failures += 1;
2034 None
2035 } else {
2036 self.folds += 1;
2037 a.checked_shr(s)
2038 }
2039 }
2040 #[allow(dead_code)]
2041 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
2042 self.folds += 1;
2043 a & b
2044 }
2045 #[allow(dead_code)]
2046 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
2047 self.folds += 1;
2048 a | b
2049 }
2050 #[allow(dead_code)]
2051 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
2052 self.folds += 1;
2053 a ^ b
2054 }
2055 #[allow(dead_code)]
2056 pub fn not_i64(&mut self, a: i64) -> i64 {
2057 self.folds += 1;
2058 !a
2059 }
2060 #[allow(dead_code)]
2061 pub fn fold_count(&self) -> usize {
2062 self.folds
2063 }
2064 #[allow(dead_code)]
2065 pub fn failure_count(&self) -> usize {
2066 self.failures
2067 }
2068 #[allow(dead_code)]
2069 pub fn enable(&mut self) {
2070 self.enabled = true;
2071 }
2072 #[allow(dead_code)]
2073 pub fn disable(&mut self) {
2074 self.enabled = false;
2075 }
2076 #[allow(dead_code)]
2077 pub fn is_enabled(&self) -> bool {
2078 self.enabled
2079 }
2080}
2081#[allow(dead_code)]
2082pub struct KtConstantFoldingHelper;
2083impl KtConstantFoldingHelper {
2084 #[allow(dead_code)]
2085 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
2086 a.checked_add(b)
2087 }
2088 #[allow(dead_code)]
2089 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
2090 a.checked_sub(b)
2091 }
2092 #[allow(dead_code)]
2093 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
2094 a.checked_mul(b)
2095 }
2096 #[allow(dead_code)]
2097 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
2098 if b == 0 {
2099 None
2100 } else {
2101 a.checked_div(b)
2102 }
2103 }
2104 #[allow(dead_code)]
2105 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
2106 a + b
2107 }
2108 #[allow(dead_code)]
2109 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
2110 a * b
2111 }
2112 #[allow(dead_code)]
2113 pub fn fold_neg_i64(a: i64) -> Option<i64> {
2114 a.checked_neg()
2115 }
2116 #[allow(dead_code)]
2117 pub fn fold_not_bool(a: bool) -> bool {
2118 !a
2119 }
2120 #[allow(dead_code)]
2121 pub fn fold_and_bool(a: bool, b: bool) -> bool {
2122 a && b
2123 }
2124 #[allow(dead_code)]
2125 pub fn fold_or_bool(a: bool, b: bool) -> bool {
2126 a || b
2127 }
2128 #[allow(dead_code)]
2129 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
2130 a.checked_shl(b)
2131 }
2132 #[allow(dead_code)]
2133 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
2134 a.checked_shr(b)
2135 }
2136 #[allow(dead_code)]
2137 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
2138 if b == 0 {
2139 None
2140 } else {
2141 Some(a % b)
2142 }
2143 }
2144 #[allow(dead_code)]
2145 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
2146 a & b
2147 }
2148 #[allow(dead_code)]
2149 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
2150 a | b
2151 }
2152 #[allow(dead_code)]
2153 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
2154 a ^ b
2155 }
2156 #[allow(dead_code)]
2157 pub fn fold_bitnot_i64(a: i64) -> i64 {
2158 !a
2159 }
2160}
2161#[allow(dead_code)]
2162pub struct KtPassRegistry {
2163 pub(super) configs: Vec<KtPassConfig>,
2164 pub(super) stats: std::collections::HashMap<String, KtPassStats>,
2165}
2166impl KtPassRegistry {
2167 #[allow(dead_code)]
2168 pub fn new() -> Self {
2169 KtPassRegistry {
2170 configs: Vec::new(),
2171 stats: std::collections::HashMap::new(),
2172 }
2173 }
2174 #[allow(dead_code)]
2175 pub fn register(&mut self, config: KtPassConfig) {
2176 self.stats
2177 .insert(config.pass_name.clone(), KtPassStats::new());
2178 self.configs.push(config);
2179 }
2180 #[allow(dead_code)]
2181 pub fn enabled_passes(&self) -> Vec<&KtPassConfig> {
2182 self.configs.iter().filter(|c| c.enabled).collect()
2183 }
2184 #[allow(dead_code)]
2185 pub fn get_stats(&self, name: &str) -> Option<&KtPassStats> {
2186 self.stats.get(name)
2187 }
2188 #[allow(dead_code)]
2189 pub fn total_passes(&self) -> usize {
2190 self.configs.len()
2191 }
2192 #[allow(dead_code)]
2193 pub fn enabled_count(&self) -> usize {
2194 self.enabled_passes().len()
2195 }
2196 #[allow(dead_code)]
2197 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
2198 if let Some(stats) = self.stats.get_mut(name) {
2199 stats.record_run(changes, time_ms, iter);
2200 }
2201 }
2202}
2203#[allow(dead_code)]
2204#[derive(Debug, Clone)]
2205pub struct KtAnalysisCache {
2206 pub(super) entries: std::collections::HashMap<String, KtCacheEntry>,
2207 pub(super) max_size: usize,
2208 pub(super) hits: u64,
2209 pub(super) misses: u64,
2210}
2211impl KtAnalysisCache {
2212 #[allow(dead_code)]
2213 pub fn new(max_size: usize) -> Self {
2214 KtAnalysisCache {
2215 entries: std::collections::HashMap::new(),
2216 max_size,
2217 hits: 0,
2218 misses: 0,
2219 }
2220 }
2221 #[allow(dead_code)]
2222 pub fn get(&mut self, key: &str) -> Option<&KtCacheEntry> {
2223 if self.entries.contains_key(key) {
2224 self.hits += 1;
2225 self.entries.get(key)
2226 } else {
2227 self.misses += 1;
2228 None
2229 }
2230 }
2231 #[allow(dead_code)]
2232 pub fn insert(&mut self, key: String, data: Vec<u8>) {
2233 if self.entries.len() >= self.max_size {
2234 if let Some(oldest) = self.entries.keys().next().cloned() {
2235 self.entries.remove(&oldest);
2236 }
2237 }
2238 self.entries.insert(
2239 key.clone(),
2240 KtCacheEntry {
2241 key,
2242 data,
2243 timestamp: 0,
2244 valid: true,
2245 },
2246 );
2247 }
2248 #[allow(dead_code)]
2249 pub fn invalidate(&mut self, key: &str) {
2250 if let Some(entry) = self.entries.get_mut(key) {
2251 entry.valid = false;
2252 }
2253 }
2254 #[allow(dead_code)]
2255 pub fn clear(&mut self) {
2256 self.entries.clear();
2257 }
2258 #[allow(dead_code)]
2259 pub fn hit_rate(&self) -> f64 {
2260 let total = self.hits + self.misses;
2261 if total == 0 {
2262 return 0.0;
2263 }
2264 self.hits as f64 / total as f64
2265 }
2266 #[allow(dead_code)]
2267 pub fn size(&self) -> usize {
2268 self.entries.len()
2269 }
2270}