Skip to main content

cure_engine/
pass.rs

1//! Pass runner:fixed-point 循环 + 成本门槛 + 编辑账本。
2
3use std::collections::{BTreeMap, HashSet};
4
5use crate::kind::NodeKind;
6use crate::rule::{apply_edit, Edit, RewriteCtx, Rule};
7use crate::walk::{walk, Walk};
8use crate::lang::Lang;
9
10#[derive(Clone, Debug)]
11pub struct Config {
12    /// 最大编辑次数保险丝(成本单调下降本身已保证终止)。
13    pub max_edits: usize,
14    /// 关闭指定名字的规则。
15    pub disabled_rules: HashSet<String>,
16    /// 删除全单元零引用的 private 方法(解密器/内联后的死 helper)。
17    /// opt-in:反射(getDeclaredMethod)无法静态排除;名字匹配不分重载,
18    /// 只会更保守地保留。默认 false。
19    pub remove_dead_methods: bool,
20}
21
22impl Default for Config {
23    fn default() -> Self {
24        Config {
25            max_edits: 100_000,
26            disabled_rules: HashSet::new(),
27            remove_dead_methods: false,
28        }
29    }
30}
31
32impl Config {
33    pub fn enabled(&self, rule: &str) -> bool {
34        !self.disabled_rules.contains(rule)
35    }
36}
37
38#[derive(Debug, Default)]
39pub struct Report {
40    /// 成功应用的编辑总数。
41    pub edits: usize,
42    /// fixed-point 扫描轮数。
43    pub iterations: usize,
44    /// 被校验拒绝丢弃的结构提案数(这些提案需新鲜 walk 复活——外层
45    /// 收敛循环据此判断是否还有下一轮的必要)。
46    pub discarded: usize,
47    /// 按规则名统计的编辑数。
48    pub by_rule: BTreeMap<&'static str, usize>,
49}
50
51
52/// 见应用环内的注释。只处理 target==当前检查节点 的直接折叠——
53/// ancestor 目标的折叠不产生「合法→非法」转变(原形态本非语句位)。
54fn legalize_stmt_fold<L: Lang>(
55    lang: &L,
56    snap: &Walk<L>,
57    id: L::Id,
58    edit: Edit<L>,
59) -> Option<Edit<L>> {
60    let Edit::Replace { target, with } = &edit else { return Some(edit) };
61    if *target != id || lang.kind(*with) != NodeKind::Literal {
62        return Some(edit);
63    }
64    // Binary→Literal 不制造新非法性(Binary 语句本就非法输入);
65    // 只有合法语句形态(调用/new)被换成字面量才是
66    if !matches!(lang.kind(id), NodeKind::Call | NodeKind::New) {
67        return Some(edit);
68    }
69    let Some(&(parent, _)) = snap.parents.get(&id) else { return Some(edit) };
70    if lang.kind(parent) != NodeKind::ExprStmt || lang.children(parent).len() != 1 {
71        return Some(edit);
72    }
73    // 语句的容器必须是 Block:Label 独子语句被删会让 Label 无孩子
74    //(printer panic——spoon ExecutableReferencePosition 抓获);Case/
75    // Synchronized 体等容器同理保守拒绝
76    let Some(&(grand, _)) = snap.parents.get(&parent) else { return Some(edit) };
77    if lang.kind(grand) != NodeKind::Block {
78        return None; // 放弃提案(返回原 Replace 会留下裸字面量,同样非法)
79    }
80    Some(Edit::Delete { node: parent })
81}
82
83
84/// 对「根即 Replace 目标」的提案求值:返回 (新根, 规则名)。
85///
86/// 引擎 pass 对 walk **根**的 Replace 无法应用(无父槽可写——字段
87/// init 场景:`static int x = 1+2` 的 init 节点就是根,Replace
88/// 找不到 parent.set_child)。本函数让门面(拥有容器结构的一方,
89/// 如 CompilationUnit 的字段声明)拿到新根自行回写,并循环到不动点。
90///
91/// 只接受 target == root 的 Replace;成本门槛与 pass 相同(严格正)。
92pub fn fold_root<L: Lang>(
93    lang: &mut L,
94    root: L::Id,
95    rules: &[Box<dyn Rule<L>>],
96    cfg: &Config,
97) -> Option<(L::Id, &'static str)> {
98    for rule in rules {
99        if !cfg.enabled(rule.name()) {
100            continue;
101        }
102        let snap = walk(lang, root);
103        let ctx = RewriteCtx { lang: &mut *lang, walk: &snap };
104        if let Some(edit) = rule.check(ctx, root) {
105            if let Edit::Replace { target, with } = edit {
106                if target == root {
107                    let red = Edit::Replace { target, with }.cost_reduction(lang);
108                    if red > 0 {
109                        return Some((with, rule.name()));
110                    }
111                }
112            }
113        }
114    }
115    None
116}
117
118/// 对 `root`(通常是方法体 Block)运行规则集直到 fixed point。
119///
120/// 终止性:每条被应用的编辑都严格降低总成本(`Edit::cost_reduction` 校验),
121/// 成本单调下降 + 非负 ⇒ 必然终止;`max_edits` 只是防御性保险丝。
122///
123/// 同 pass 批量应用(正确性论证见 rule.rs 尾部注释):
124/// - 纯 Replace 立即应用(原位换孩子不移动兄弟下标,快照索引全程有效),
125///   并沿祖先链失效效果缓存后继续本 pass 扫描;
126/// - 含 Delete/Splice 的结构编辑记录足迹与 Splice 区间快照,pass 末按
127///   (parent, 位置) 降序校验应用,校验失配(树不再符合提案假设)→ 丢弃,
128///   下一 pass 重新提案——丢弃不降成本,单调性不受影响;
129/// - 足迹(含子树)进 touched 集:已承诺区域不再扫描、不再被新提案触碰。
130pub fn simplify<L: Lang>(
131    lang: &mut L,
132    root: L::Id,
133    rules: &[Box<dyn Rule<L>>],
134    cfg: &Config,
135) -> Report {
136    let mut report = Report::default();
137    lang.prepare(root);
138    // 类别分派桶:kind → 关注该类别的规则下标(含 kinds()==空 的通用规则)。
139    // 惰性建桶(每类别至多一次,O(规则数));Vec 按 discriminant 索引,
140    // 新变体自动扩容——没有"漏桶"风险。
141    let mut dispatch: Vec<Option<Box<[u32]>>> = vec![None; NodeKind::SLOT_COUNT];
142    loop {
143        let mut snap = walk(lang, root);
144        // touched = 立即应用的编辑足迹(真已脱离树)。位图索引=node_index
145        // (旧版 IdMap 哈希集:扫描循环每节点一次哈希查找 × 30k×136 轮)。
146        // 已排队(未应用)的编辑不改树 → 其足迹不进 touched。
147        let mut touched: Vec<u64> = Vec::new();
148        // 搬移集:立即 Replace 把既有节点挪进新位置(with 复用旧节点)。
149        // 这些节点的快照 parent 已过期——后续提案不得以其为目标(应用会
150        // 写进幽灵父=半应用)。下一 pass 新鲜 walk 后自然解除。
151        let mut moved: Vec<u64> = Vec::new();
152        struct Queued<L: Lang> {
153            /// 规则下标/提案节点:级联重查模式遗留(现按提案直接校验应用),
154            /// 保留字段供 CURE_DEBUG_PASS 追踪与未来级联复用
155            #[allow(dead_code)]
156            rule_idx: usize,
157            #[allow(dead_code)]
158            node: L::Id,
159            rule: &'static str,
160            edit: Edit<L>,
161            expects: Vec<Vec<L::Id>>,
162        }
163        let mut queued: Vec<Queued<L>> = Vec::new();
164        let mut applied = false;
165        #[allow(unused_mut)]
166        let applied_immediate = false;
167
168        'scan: for &id in &snap.ids {
169            if bit_get(&touched, lang.node_index(id)) {
170                continue;
171            }
172            let kind = lang.kind(id);
173            let ki = kind.slot();
174            debug_assert!(ki < NodeKind::SLOT_COUNT);
175            let bucket: &[u32] = {
176                let slot = &mut dispatch[ki];
177                if slot.is_none() {
178                    *slot = Some(
179                        rules
180                            .iter()
181                            .enumerate()
182                            .filter(|(_, r)| {
183                                let ks = r.kinds();
184                                ks.is_empty() || ks.contains(&kind)
185                            })
186                            .map(|(i, _)| i as u32)
187                            .collect::<Vec<u32>>()
188                            .into_boxed_slice(),
189                    );
190                }
191                slot.as_deref().unwrap()
192            };
193            for &ri in bucket {
194                let rule = &rules[ri as usize];
195                if !cfg.enabled(rule.name()) {
196                    continue;
197                }
198                let ctx = RewriteCtx {
199                    lang: &mut *lang,
200                    walk: &snap,
201                };
202                let proposal = rule.check(ctx, id);
203                let Some(edit) = proposal else {
204                    continue;
205                };
206                // JLS 14.8 合法化:Call/New 折叠为字面量且恰为
207                // ExpressionStatement 独子 → 裸字面量语句非法
208                //(`String.valueOf(temp);` → `"7";`)→ 值被丢弃的
209                // 纯调用语句整体删除(能折叠成字面量的调用必然无副作用)
210                let Some(edit) = legalize_stmt_fold(&*lang, &snap, id, edit) else {
211                    continue;
212                };
213                let structural = rule.structural();
214                let reduction = edit.cost_reduction(lang);
215                debug_assert!(
216                    structural || reduction > 0,
217                    "rule `{}` proposed a non-simplifying edit",
218                    rule.name()
219                );
220                if !structural && reduction == 0 {
221                    continue;
222                }
223                if crate::rule::is_replace_only(&edit) {
224                    // 足迹冲突:立即应用会真改树,与已应用足迹/搬移节点相交 →
225                    // 放弃本轮(下一 pass 新鲜快照重提)
226                    let mut fp = Vec::new();
227                    crate::rule::collect_footprint(&*lang, &edit, &mut fp);
228                    if fp
229                        .iter()
230                        .any(|n| bit_get(&touched, lang.node_index(*n)) || bit_get(&moved, lang.node_index(*n)))
231                    {
232                        continue;
233                    }
234                    if apply_edit(lang, &snap, &edit).is_ok() {
235                        // with 子树里的既有节点(不在快照中)已被搬移
236                        let mut stack = with_roots_of(&edit);
237                        while let Some(n) = stack.pop() {
238                            if n != snap.root && !snap.parents.contains_key(&n) {
239                                bit_set(&mut moved, lang.node_index(n));
240                            }
241                            for &c in lang.children(n) {
242                                stack.push(c);
243                            }
244                        }
245                        if std::env::var("CURE_DEBUG_PASS").is_ok() {
246                            eprintln!(
247                                "[pass] immediate {} :: {}",
248                                rule.name(),
249                                describe_edit(&*lang, &edit)
250                            );
251                        }
252                        *report.by_rule.entry(rule.name()).or_insert(0) += 1;
253                        report.edits += 1;
254                        applied = true;
255                        for n in fp {
256                            bit_set(&mut touched, lang.node_index(n));
257                        }
258                        // 祖先链效果失效:被换子树的祖先聚合效果已陈旧;
259                        // with 子树中既有节点被**搬移**——其旧容器(快照祖先)
260                        // 的事件条目同样陈旧(B2 教训:漏掉曾驱动 cff_diamond
261                        // 错误决策)。惰性重建(B3)下必须失效,否则旧条目
262                        // 永不重建。
263                        // 失效根:提案节点 + **全部**子编辑目标/with(Multi
264                        // [Replace A, Replace B] 的子目标各自需要失效——
265                        // 惰性重建下漏失效 = 陈旧事件驱动错误决策,deobfuscate
266                        // 差分当场抓获:q 声明被误删);with 含既有节点 =
267                        // 搬移,旧容器条目同样陈旧(B2 教训)
268                        let roots = {
269                            let mut r = vec![id];
270                            r.extend(edit_invalidation_roots(&edit));
271                            r
272                        };
273                        for root in roots {
274                            // 根自身先失效:规则可能**原地修改**搬移节点
275                            //(decl_assign_merge 给既有 VarDecl 加 init——
276                            // 惰性重建下条目陈旧,deobfuscate 差分 + 事件
277                            // 自检抓获:indexed 2 vs fresh 3)
278                            lang.invalidate_effect(root);
279                            let mut cur = Some(root);
280                            while let Some(a) =
281                                cur.and_then(|c| snap.parents.get(&c).map(|p| p.0))
282                            {
283                                lang.invalidate_effect(a);
284                                cur = Some(a);
285                            }
286                        }
287                        continue 'scan;
288                    }
289                } else {
290                    // 足迹冲突:与其他已排队提案相交 → 放弃(守卫对提案时树成立,
291                    // 应用时不再重验——无重叠 + 单次新鲜重建保证一致性)
292                    let mut fp = Vec::new();
293                    crate::rule::collect_footprint(&*lang, &edit, &mut fp);
294                    if fp
295                        .iter()
296                        .any(|n| bit_get(&touched, lang.node_index(*n)) || bit_get(&moved, lang.node_index(*n)))
297                    {
298                        continue;
299                    }
300                    let mut expects = Vec::new();
301                    crate::rule::collect_splice_expectations(&*lang, &edit, &mut expects);
302                    queued.push(Queued {
303                        rule_idx: ri as usize,
304                        node: id,
305                        rule: rule.name(),
306                        edit,
307                        expects,
308                    });
309                    for n in fp {
310                        bit_set(&mut touched, lang.node_index(n));
311                    }
312                    continue 'scan;
313                }
314            }
315        }
316
317        // pass 末:应用结构编辑队列。
318        // 立即路径的编辑已改树(含既有节点搬移到新位置——其快照 parent 已
319        // 过期):先重建 walk+prepare 使一切新鲜,再逐条校验应用。
320        // 校验:Replace/Delete 按节点身份搜索定位;Splice 要求被删区间仍
321        // 等于提案时记录的节点序列。失配(提案假设不再成立)→ 丢弃,
322        // 下一 pass 重新提案——丢弃不降成本,单调性不受影响。
323        // 重建条件:仅本 pass 有立即编辑(搬移过既有节点)时快照才过期;
324        // 校验是纯结构操作(孩子表查找),不需要效果/类型缓存——只建 walk。
325        if !queued.is_empty() {
326            if applied_immediate {
327                snap = walk(lang, root);
328            }
329            queued.sort_by(|a, b| {
330                let ka = crate::rule::structural_sort_key(&snap, &a.edit);
331                let kb = crate::rule::structural_sort_key(&snap, &b.edit);
332                kb.cmp(&ka)
333            });
334            for q in queued {
335                let mut expects = q.expects.as_slice();
336                let ok = crate::rule::verify_deferred(&*lang, &snap, &q.edit, &mut expects);
337                if std::env::var("CURE_DEBUG_PASS").is_ok() {
338                    eprintln!(
339                        "[pass] deferred {} {} :: {}",
340                        if ok { "apply" } else { "DISCARD" },
341                        q.rule,
342                        describe_edit(&*lang, &q.edit)
343                    );
344                }
345                if ok {
346                    // 失效集(应用**前**取——用旧快照定位祖先):目标 +
347                    // 删除 + insert/with 中的既有节点(搬移旧容器——B2 教训;
348                    // 新节点无祖先链,循环自然为零)
349                    let roots = edit_invalidation_roots(&q.edit);
350                    crate::rule::apply_deferred(&mut *lang, &snap, &q.edit);
351                    for root in roots {
352                        lang.invalidate_effect(root);
353                        let mut cur = Some(root);
354                        while let Some(a) =
355                            cur.and_then(|c| snap.parents.get(&c).map(|p| p.0))
356                        {
357                            lang.invalidate_effect(a);
358                            cur = Some(a);
359                        }
360                    }
361                    *report.by_rule.entry(q.rule).or_insert(0) += 1;
362                    report.edits += 1;
363                    applied = true;
364                } else {
365                    report.discarded += 1;
366                }
367            }
368        }
369
370        report.iterations += 1;
371        if !applied || report.edits >= cfg.max_edits {
372            break;
373        }
374        lang.prepare(root);
375    }
376    report
377}
378
379// 【教训】脏式索引重建(B2:只重建被失效的条目)经 cff_diamond 差分
380// 当场抓获失效不完备(队列编辑搬移 with 子树时,其旧容器的条目不在目标
381// 祖先链上 → 陈旧事件驱动错误决策,如误删初始化)。补目标祖先链失效
382// 仍不够——with 来源容器的失效需要跨树追踪。已放弃 B2,保留全量重建
383// + 容量保留(B1)。
384
385/// 位图置位(按需扩容)。
386fn bit_set(bits: &mut Vec<u64>, idx: usize) {
387    if idx >= bits.len() * 64 {
388        bits.resize(idx / 64 + 1, 0);
389    }
390    bits[idx / 64] |= 1u64 << (idx % 64);
391}
392
393/// 位图读位(越界 = false)。
394fn bit_get(bits: &[u64], idx: usize) -> bool {
395    bits.get(idx / 64)
396        .is_some_and(|w| (w >> (idx % 64)) & 1 == 1)
397}
398
399/// 编辑全部 with 根(Replace / 全 Replace Multi)。
400/// 编辑的全部失效根(应用**前**用旧快照定位):目标/删除节点 +
401/// insert/with 中**既有**节点(搬移——其旧容器条目陈旧,B2 教训)。
402/// 新建节点不在 snap.parents 中 → 祖先链循环自然为空,无需特判。
403/// 编辑的全部失效根(应用**前**用旧快照定位祖先链):目标/删除节点 +
404/// insert/with 中的全部节点(既有节点 = 搬移,旧容器条目陈旧——B2 教训;
405/// 新节点不在快照中,祖先链循环自然为零次,无需特判)。
406fn edit_invalidation_roots<L: Lang>(edit: &Edit<L>) -> Vec<L::Id> {
407    let mut out = Vec::new();
408    collect_all(edit, &mut out);
409    out
410}
411
412fn collect_all<L: Lang>(edit: &Edit<L>, out: &mut Vec<L::Id>) {
413    match edit {
414        Edit::Replace { target, with } => {
415            out.push(*target);
416            out.push(*with);
417        }
418        Edit::Delete { node } => out.push(*node),
419        Edit::Splice { node, insert, .. } => {
420            out.push(*node);
421            out.extend(insert.iter().copied());
422        }
423        Edit::Multi(es) => {
424            for e in es {
425                collect_all(e, out);
426            }
427        }
428    }
429}
430
431fn with_roots_of<L: Lang>(edit: &Edit<L>) -> Vec<L::Id> {
432    match edit {
433        Edit::Replace { with, .. } => vec![*with],
434        Edit::Multi(es) => es
435            .iter()
436            .filter_map(|e| match e {
437                Edit::Replace { with, .. } => Some(*with),
438                _ => None,
439            })
440            .collect(),
441        _ => Vec::new(),
442    }
443}
444
445/// 调试用:编辑的紧凑描述(节点 kind + 变体形态,不要求 L: Debug)。
446fn describe_edit<L: Lang>(lang: &L, edit: &Edit<L>) -> String {
447    fn kind_of<L: Lang>(lang: &L, id: L::Id) -> &'static str {
448        match lang.kind(id) {
449            NodeKind::Block => "Block",
450            NodeKind::VarDecl => "VarDecl",
451            NodeKind::Assign => "Assign",
452            NodeKind::ExprStmt => "ExprStmt",
453            NodeKind::Return => "Return",
454            NodeKind::If => "If",
455            NodeKind::While => "While",
456            NodeKind::VarRef => "VarRef",
457            NodeKind::Literal => "Literal",
458            NodeKind::Binary => "Binary",
459            NodeKind::Call => "Call",
460            NodeKind::Index => "Index",
461            NodeKind::Member => "Member",
462            _ => "…",
463        }
464    }
465    fn go<L: Lang>(lang: &L, e: &Edit<L>, out: &mut String) {
466        match e {
467            Edit::Replace { target, with } => {
468                out.push_str(&format!(
469                    "Replace {}→{} ",
470                    kind_of(lang, *target),
471                    kind_of(lang, *with)
472                ));
473            }
474            Edit::Delete { node } => {
475                out.push_str(&format!("Delete {} ", kind_of(lang, *node)));
476            }
477            Edit::Splice { index, remove, .. } => {
478                out.push_str(&format!("Splice @{index} -{remove} "));
479            }
480            Edit::Multi(es) => {
481                out.push('[');
482                for e in es {
483                    go(lang, e, out);
484                }
485                out.push(']');
486            }
487        }
488    }
489    let mut s = String::new();
490    go(lang, edit, &mut s);
491    s
492}