Skip to main content

cure_java_simplify/
lib.rs

1//! # cure-java-simplify
2//!
3//! Java 简化门面:引擎通用规则 + Java 特有规则,一站式 [`simplify`]。
4//!
5//! 引擎规则(布尔、自赋值、常量条件、局部传播……)定义在 cure-engine,
6//! 通过 `Lang` 抽象复用;本 crate 只放需要 Java 类型/负载信息的规则。
7
8mod jdk;
9pub use jdk::set_jdk_home;
10mod vexec;
11
12use cure_engine::kind::{BinOp, UnOp};
13use cure_engine::{Config, Edit, Effect, Lang, LitRef, NodeKind, Report, RewriteCtx, Rule};
14use cure_java_ast::{CompilationUnit, JavaAst, JavaId, JType, Lit, Member, NoopCandidate, NodeData, TypeDecl};
15
16// ---------------------------------------------------------------------------
17// Java 特有规则
18// ---------------------------------------------------------------------------
19
20/// 冗余 cast:
21/// - `(T)(T) x → (T) x`(嵌套同型)
22/// - `(T) x → x`(x 的声明类型就是 T,作用域解析后可知)
23pub struct CastSimplify;
24
25impl Rule<JavaAst> for CastSimplify {
26    fn name(&self) -> &'static str {
27        "cast_simplify"
28    }
29    fn kinds(&self) -> &'static [NodeKind] {
30        &[NodeKind::Cast]
31    }
32    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
33        let lang = ctx.lang;
34        let NodeData::Cast { ty } = lang.data(id) else {
35            return None;
36        };
37        let ty = ty.clone();
38        let inner = lang.children(id)[0];
39
40        // (T)(T) x → (T) x
41        if let NodeData::Cast { ty: inner_ty } = lang.data(inner) {
42            if *inner_ty == ty {
43                return Some(Edit::Replace {
44                    target: id,
45                    with: inner,
46                });
47            }
48        }
49
50        // (T) x,x 声明类型 == T → 去掉 cast
51        if let NodeData::VarRef { .. } = lang.data(inner) {
52            if lang.var_type(inner) == Some(&ty) {
53                return Some(Edit::Replace {
54                    target: id,
55                    with: inner,
56                });
57            }
58        }
59        None
60    }
61}
62
63/// 同一整数局部变量的 `x == x → true` / `x != x → false`
64/// (浮点有 NaN 语义,引用保守起见不碰)。
65pub struct SelfCompare;
66
67impl Rule<JavaAst> for SelfCompare {
68    fn name(&self) -> &'static str {
69        "self_compare"
70    }
71    fn kinds(&self) -> &'static [NodeKind] {
72        &[NodeKind::Binary]
73    }
74    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
75        use cure_engine::kind::BinOp::{Eq, Ne};
76        let lang = ctx.lang;
77        let NodeData::Binary { op } = lang.data(id) else {
78            return None;
79        };
80        if !matches!(op, Eq | Ne) {
81            return None;
82        }
83        let ch = lang.children(id);
84        let (NodeData::VarRef { name: n1 }, NodeData::VarRef { name: n2 }) =
85            (lang.data(ch[0]), lang.data(ch[1]))
86        else {
87            return None;
88        };
89        if n1 != n2 || !lang.is_exact_int(ch[0]) {
90            return None;
91        }
92        let val = lang.build_bool(*op == Eq);
93        Some(Edit::Replace {
94            target: id,
95            with: val,
96        })
97    }
98}
99
100
101// ---------------------------------------------------------------------------
102// StringBuilder 链还原(反编译器高频产物):
103//   new StringBuilder().append(a).append(b).toString()  →  a + b
104//   new StringBuilder("s").append(x).toString()          →  "s" + x
105// 安全性:两侧每个实参恰好按序求值一次;null/基本类型转换语义一致
106// (append(String.valueOf 语义) == 字符串拼接的 String.valueOf 语义)。
107// 首参非 String 类型时前置 ""(`1 + 2` 是整数加法,`"" + 1 + 2` 才是拼接)。
108// new StringBuilder(int容量) 形态**不折叠**(容量构造,语义不同)。
109// ---------------------------------------------------------------------------
110
111pub struct StringBuilderFold;
112
113impl Rule<JavaAst> for StringBuilderFold {
114    fn name(&self) -> &'static str {
115        "string_builder_fold"
116    }
117    fn kinds(&self) -> &'static [NodeKind] {
118        &[NodeKind::Call]
119    }
120    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
121        let root = ctx.root();
122        let lang = ctx.lang;
123        // 外层:X.toString()
124        if lang.kind(id) != NodeKind::Call {
125            return None;
126        }
127        let callee = lang.children(id)[0];
128        if let NodeData::Member { name } = lang.data(callee) {
129            if lang.sn(*name) != "toString" || !lang.children(id)[1..].is_empty() {
130                return None;
131            }
132        } else {
133            return None;
134        }
135        let mut node = lang.children(callee)[0]; // 链头:… .append(...)
136        let mut parts: Vec<JavaId> = Vec::new();
137        // 沿 append 链下溯
138        loop {
139            match lang.data(node) {
140                NodeData::Call => {
141                    let c = lang.children(node);
142                    if let NodeData::Member { name } = lang.data(c[0]) {
143                        if lang.sn(*name) == "append" && c.len() == 2 {
144                            parts.push(c[1]);
145                            node = lang.children(c[0])[0];
146                            continue;
147                        }
148                    }
149                    return None;
150                }
151                NodeData::New { ty, .. } => {
152                    let is_sb = !lang.unit_shadows_lang
153                && matches!(ty, JType::Ref(n) if n == "StringBuilder" || n == "java.lang.StringBuilder");
154                    if !is_sb {
155                        return None;
156                    }
157                    let args = lang.children(node).to_vec();
158                    match args.len() {
159                        0 => {} // new StringBuilder()
160                        1 => {
161                            // String 内容构造参与拼接;int/long 字面量必为
162                            // 容量构造(无接收 int 的内容重载)——容量只是
163                            // 分配提示,无语义差异,按无参处理;变量实参
164                            // 类型不可证,保守拒绝
165                            let a = args[0];
166                            let stringy = matches!(lang.literal(a), Some(LitRef::Str(_)))
167                                || lang
168                                    .var_type(a)
169                                    .is_some_and(|t| matches!(t, JType::Ref(n) if n == "String"));
170                            if stringy {
171                                parts.push(a);
172                            } else if !matches!(
173                                lang.literal(a),
174                                Some(LitRef::Int(_) | LitRef::Long(_))
175                            ) {
176                                return None;
177                            }
178                        }
179                        _ => return None,
180                    }
181                    break;
182                }
183                _ => return None,
184            }
185        }
186        // 全字面量链折叠结果是常量表达式(javac 池化),而 sb.toString() 是
187        // 运行期新建未池化的串——== 语义会变 → 身份守卫。混合链(含变量/调用)
188        // 折叠后是非常量拼接,运行期行为与 toString 一致,无需守卫。
189        if parts.iter().all(|&p| lang.literal(p).is_some())
190            && lang.has_string_identity_compare(root)
191        {
192            return None;
193        }
194        if parts.is_empty() {
195            // new StringBuilder().toString() → ""
196            let empty = lang.build_str("");
197            return Some(Edit::Replace {
198                target: id,
199                with: empty,
200            });
201        }
202        // 逆序收集:parts 从链尾到链头,反转得源顺序
203        parts.reverse();
204        let first = parts[0];
205        let stringy_first = matches!(lang.literal(first), Some(LitRef::Str(_)))
206            || lang
207                .var_type(first)
208                .is_some_and(|t| matches!(t, JType::Ref(n) if n == "String"));
209        let mut acc = if stringy_first {
210            first
211        } else {
212            let empty = lang.build_str("");
213            lang.build_bin(BinOp::Add, empty, first)
214        };
215        for &p in &parts[1..] {
216            acc = lang.build_bin(BinOp::Add, acc, p);
217        }
218        Some(Edit::Replace {
219            target: id,
220            with: acc,
221        })
222    }
223}
224
225// ---------------------------------------------------------------------------
226// 装箱-拆箱链还原(反编译器产物):
227//   Integer.valueOf(n).intValue() → n(Long/Boolean/Double/Float/Character/Short/Byte 同理)
228//   new Integer(n).intValue()     → n
229// 类型守卫:实参必须匹配包装类型的原始形态(valueOf(String) 是解析,语义不同)。
230// ---------------------------------------------------------------------------
231
232pub struct BoxUnboxChain;
233
234const BOXERS: &[(&str, &str)] = &[
235    ("Integer", "intValue"),
236    ("java.lang.Integer", "intValue"),
237    ("Long", "longValue"),
238    ("java.lang.Long", "longValue"),
239    ("Boolean", "booleanValue"),
240    ("java.lang.Boolean", "booleanValue"),
241    ("Double", "doubleValue"),
242    ("java.lang.Double", "doubleValue"),
243    ("Float", "floatValue"),
244    ("java.lang.Float", "floatValue"),
245    ("Character", "charValue"),
246    ("java.lang.Character", "charValue"),
247    ("Short", "shortValue"),
248    ("java.lang.Short", "shortValue"),
249    ("Byte", "byteValue"),
250    ("java.lang.Byte", "byteValue"),
251];
252
253impl Rule<JavaAst> for BoxUnboxChain {
254    fn name(&self) -> &'static str {
255        "box_unbox_chain"
256    }
257    fn kinds(&self) -> &'static [NodeKind] {
258        &[NodeKind::Call]
259    }
260    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
261        let lang = ctx.lang;
262        // 外层:inner.xxxValue()
263        if lang.kind(id) != NodeKind::Call {
264            return None;
265        }
266        let callee = lang.children(id)[0];
267        let NodeData::Member { name: unbox } = lang.data(callee) else {
268            return None;
269        };
270        if !lang.children(id)[1..].is_empty() {
271            return None;
272        }
273        let inner = lang.children(callee)[0];
274        if lang.kind(inner) != NodeKind::Call {
275            return None;
276        }
277        // 内层:Box.valueOf(arg) 或 new Box(arg)
278        let ich = lang.children(inner).to_vec();
279        let (box_name, arg): (String, JavaId) = if let NodeData::New { ty, .. } = lang.data(inner)
280        {
281            let n = match ty {
282                JType::Ref(r) => r.clone(),
283                _ => return None,
284            };
285            if ich.len() != 1 {
286                return None;
287            }
288            (n, ich[0])
289        } else {
290            // valueOf 静态调用:callee 是 Member 或 VarRef
291            let icallee = ich.first().copied()?;
292            if let NodeData::Member { name } = lang.data(icallee) {
293                // Integer.valueOf:recv 是 VarRef(Integer)
294                let recv = lang.children(icallee)[0];
295                if lang.sn(*name) != "valueOf" {
296                    return None;
297                }
298                {
299                    let n = lang.var_name(recv)?;
300                    (n.to_string(), *ich.get(1)?)
301                }
302            } else if let NodeData::VarRef { name } = lang.data(icallee) {
303                // java.lang.Integer.valueOf 形态在解析里是 member 链;
304                // 裸 valueOf( 视为同包静态——保守跳过
305                let _ = name;
306                return None;
307            } else {
308                return None;
309            }
310        };
311        let unbox_name = *unbox;
312        let expected = BOXERS
313            .iter()
314            .find(|(b, _)| *b == box_name.as_str())
315            .map(|(_, u)| *u)?;
316        if expected != lang.sn(unbox_name) {
317            return None;
318        }
319        // 类型守卫:实参必须是该包装类型对应的原始类型表达式
320        // (valueOf(String) 是解析语义,原始类型形态天然排除字符串)。
321        let var_is = |t: fn(&JType) -> bool| lang.var_type(arg).is_some_and(t);
322        let lit_is_num = lang.literal(arg).is_some_and(|l| {
323            matches!(
324                l,
325                LitRef::Int(_) | LitRef::Long(_) | LitRef::Float(_) | LitRef::Double(_)
326            )
327        });
328        let ok = match box_name.trim_start_matches("java.lang.") {
329            "Integer" | "Long" | "Short" | "Byte" => lang.is_exact_int(arg),
330            "Double" | "Float" => {
331                lit_is_num
332                    || var_is(|t| matches!(t, JType::Double | JType::Float | JType::Int | JType::Long))
333            }
334            "Boolean" => lang.is_bool(arg),
335            "Character" => {
336                matches!(lang.literal(arg), Some(LitRef::Char(_))) || var_is(|t| *t == JType::Char)
337            }
338            _ => false,
339        };
340        if !ok {
341            return None;
342        }
343        Some(Edit::Replace {
344            target: id,
345            with: arg,
346        })
347    }
348}
349
350// ---------------------------------------------------------------------------
351// 迭代器模式还原(反编译器经典产物):
352//   for (Iterator<E> it = c.iterator(); it.hasNext(); ) {
353//       E e = it.next();
354//       …rest…
355//   }
356//   →  for (E e : c) { …rest… }
357// 安全性:与 for-each 的脱糖完全同构(iterator() 各求值一次、hasNext/next 同序)。
358// 守卫:`it` 在 body 其余部分不再被引用。
359// ---------------------------------------------------------------------------
360
361pub struct IteratorToForEach;
362
363impl Rule<JavaAst> for IteratorToForEach {
364    fn name(&self) -> &'static str {
365        "iterator_to_for_each"
366    }
367    fn kinds(&self) -> &'static [NodeKind] {
368        &[NodeKind::For]
369    }
370    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
371        let lang = ctx.lang;
372        let NodeData::For {
373            inits,
374            has_cond,
375            steps,
376        } = lang.data(id)
377        else {
378            return None;
379        };
380        if *inits != 1 || !*has_cond || *steps != 0 {
381            return None;
382        }
383        let ch = lang.children(id).to_vec();
384        // children: [init, cond, body]
385        let (init, cond, body) = (ch[0], ch[1], ch[2]);
386
387        // init: Iterator<…> it = <iterable>.iterator();
388        let NodeData::VarDecl { name: it_name, .. } = lang.data(init) else {
389            return None;
390        };
391        let it_name = it_name.clone();
392        let init_call = lang.children(init).first().copied()?;
393        if lang.kind(init_call) != NodeKind::Call {
394            return None;
395        }
396        let ic = lang.children(init_call).to_vec();
397        let icallee = *ic.first()?;
398        let NodeData::Member { name: m0 } = lang.data(icallee) else {
399            return None;
400        };
401        if lang.sn(*m0) != "iterator" || ic.len() != 1 {
402            return None;
403        }
404        let iterable = lang.children(icallee)[0];
405        // iterable 不得引用 it(自引用)
406        if subtree_has_var(&*lang, iterable, &it_name) {
407            return None;
408        }
409
410        // cond: it.hasNext()
411        if lang.kind(cond) != NodeKind::Call {
412            return None;
413        }
414        let cc = lang.children(cond).to_vec();
415        let ccallee = *cc.first()?;
416        let NodeData::Member { name: m1 } = lang.data(ccallee) else {
417            return None;
418        };
419        if lang.sn(*m1) != "hasNext" || cc.len() != 1 {
420            return None;
421        }
422        if lang.var_name(lang.children(ccallee)[0]) != Some(it_name.as_str()) {
423            return None;
424        }
425
426        // body: Block[ VarDecl e = it.next();, rest… ]
427        if lang.kind(body) != NodeKind::Block {
428            return None;
429        }
430        let bch = lang.children(body).to_vec();
431        let first = *bch.first()?;
432        let NodeData::VarDecl { name: e_name, ty, .. } = lang.data(first) else {
433            return None;
434        };
435        let (e_name, ty) = (e_name.clone(), ty.clone());
436        let next_call = lang.children(first).first().copied()?;
437        if lang.kind(next_call) != NodeKind::Call {
438            return None;
439        }
440        let nc = lang.children(next_call).to_vec();
441        let ncallee = *nc.first()?;
442        let NodeData::Member { name: m2 } = lang.data(ncallee) else {
443            return None;
444        };
445        if lang.sn(*m2) != "next" || nc.len() != 1 {
446            return None;
447        }
448        if lang.var_name(lang.children(ncallee)[0]) != Some(it_name.as_str()) {
449            return None;
450        }
451        // rest 不得引用 it
452        let rest = bch[1..].to_vec();
453        for &r in &rest {
454            if subtree_has_var(&*lang, r, &it_name) {
455                return None;
456            }
457        }
458        let new_body = lang.build_block(rest);
459        let with = lang.for_each(&e_name, ty, iterable, new_body);
460        Some(Edit::Replace {
461            target: id,
462            with,
463        })
464    }
465}
466
467/// 剥掉 next() 调用外围的 Paren / Cast 包装(DAD:`String s = (String) it.next();`)
468fn unwrap_paren_cast(lang: &JavaAst, mut n: JavaId) -> JavaId {
469    loop {
470        match lang.kind(n) {
471            NodeKind::Paren | NodeKind::Cast => {
472                let ch = lang.children(n);
473                if ch.is_empty() {
474                    return n;
475                }
476                n = ch[0];
477            }
478            _ => return n,
479        }
480    }
481}
482
483/// 从任意泛型 Ref(ArrayList<String> / List<T>)提取元素类型;裸类型 None。
484fn generic_elem_ty(ty: &JType) -> Option<JType> {
485    let name = match ty {
486        JType::Ref(n) => n.as_str(),
487        _ => return None,
488    };
489    let lt = name.find('<')?;
490    let gt = name.rfind('>')?;
491    if gt <= lt {
492        return None;
493    }
494    let inner = name[lt + 1..gt].trim();
495    if inner.is_empty() || inner.contains('<') {
496        return None; // 嵌套泛型/通配符 → Object 兜底
497    }
498    Some(JType::Ref(inner.to_string()))
499}
500
501/// for-each 元素类型合法性:`?`/`? extends …`/`? super …`(通配符)不是
502/// 合法变量类型 → Object(jdk-sources Subject 抓获:`Iterator<?> ce =
503/// c.iterator()` 还原成 `for (? e2 : c)` 非法 Java)
504fn for_each_elem_ok(ty: &JType) -> bool {
505    match ty {
506        JType::Ref(n) => !n.trim_start().starts_with('?'),
507        _ => true,
508    }
509}
510
511/// 收集子树内 name 的全部 VarRef。
512fn collect_var_refs(lang: &JavaAst, root: JavaId, name: &str, out: &mut Vec<JavaId>) {
513    let mut stack = vec![root];
514    while let Some(n) = stack.pop() {
515        if lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name) {
516            out.push(n);
517        }
518        for &c in lang.children(n) {
519            stack.push(c);
520        }
521    }
522}
523
524/// 在 root 子树内找 target 的直接父节点。
525fn parent_of_recv(lang: &JavaAst, root: JavaId, target: JavaId) -> JavaId {
526    let mut stack = vec![root];
527    while let Some(n) = stack.pop() {
528        for &c in lang.children(n) {
529            if c == target {
530                return n;
531            }
532            stack.push(c);
533        }
534    }
535    root
536}
537
538fn subtree_has_var(lang: &JavaAst, id: JavaId, name: &str) -> bool {
539    let mut stack = vec![id];
540    while let Some(n) = stack.pop() {
541        if lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name) {
542            return true;
543        }
544        for &c in lang.children(n) {
545            stack.push(c);
546        }
547    }
548    false
549}
550
551/// 名字绑定检查(catch 形参 / for-each 变量——不在 VarRef/VarDecl 扫描域)
552fn subtree_has_binding(lang: &JavaAst, id: JavaId, name: &str) -> bool {
553    let mut stack = vec![id];
554    while let Some(n) = stack.pop() {
555        match lang.data(n) {
556            NodeData::Catch { name: cn, .. } if cn == name => return true,
557            NodeData::ForEach { name: vn, .. } if vn == name => return true,
558            // Lambda 显式参数(params_raw 原文切分——T3a 抓获:park(e -> 0)
559            // 的未读形参曾使 for-each 变量 e 撞名)
560            NodeData::Lambda { params_raw } => {
561                for part in
562                    params_raw.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$')
563                {
564                    if part == name {
565                        return true;
566                    }
567                }
568            }
569            // instanceof 模式绑定(bind 属性非子节点——T3d)
570            NodeData::InstanceOf { bind: Some(b), .. } if b == name => return true,
571            // TWR 资源/普通声明(未读资源 var_decl 因 close() 副作用不可删
572            // 而存活到碰撞点——T3c;普通未读声明先行删除,此处保守命中无害)
573            NodeData::VarDecl { name: dn, .. } if dn == name => return true,
574            _ => {}
575        }
576        for &c in lang.children(n) {
577            stack.push(c);
578        }
579    }
580    false
581}
582
583// ---------------------------------------------------------------------------
584// new String 折叠(混淆器/反编译器产物):new String("lit") → "lit"、new String() → ""
585// 仅字面量实参(new String(charArray/bytes) 是拷贝语义,不折)。
586// ---------------------------------------------------------------------------
587
588pub struct NewStringFold;
589
590impl Rule<JavaAst> for NewStringFold {
591    fn name(&self) -> &'static str {
592        "new_string_fold"
593    }
594    fn kinds(&self) -> &'static [NodeKind] {
595        &[NodeKind::New]
596    }
597    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
598        let root = ctx.root();
599        let lang = ctx.lang;
600        let NodeData::New { ty, .. } = lang.data(id) else {
601            return None;
602        };
603        if !matches!(ty, JType::Ref(n) if n == "String" || n == "java.lang.String") {
604            return None;
605        }
606        // 同单元声明 String/StringBuilder 类型 → 遮蔽 java.lang,目标
607        // 不是 java.lang.String(探针 T4a:class String{} 同文件时折叠
608        // 产出不可编译)
609        if lang.unit_shadows_lang {
610            return None;
611        }
612        // 折叠产出池化字面量,改变 String 引用身份 → 守卫(单元级:
613        // 字段 init 的 == 观察点在其他方法——单根扫描看不见)
614        let _ = root;
615        if lang.unit_string_identity {
616            return None;
617        }
618        let args = lang.children(id).to_vec();
619        match args.len() {
620            0 => {
621                let empty = lang.build_str("");
622                Some(Edit::Replace {
623                    target: id,
624                    with: empty,
625                })
626            }
627            1 => {
628                if matches!(lang.literal(args[0]), Some(LitRef::Str(_))) {
629                    Some(Edit::Replace {
630                        target: id,
631                        with: args[0],
632                    })
633                } else {
634                    None
635                }
636            }
637            _ => None,
638        }
639    }
640}
641
642
643// ---------------------------------------------------------------------------
644// new String(char 字面量数组) → 字符串字面量(ZKM/Allatori 的字符串藏匿形态)。
645// 守卫与 new String(lit) 相同:产出池化字面量改变 == 引用语义。
646// ---------------------------------------------------------------------------
647
648pub struct NewStringCharArrayFold;
649
650impl Rule<JavaAst> for NewStringCharArrayFold {
651    fn name(&self) -> &'static str {
652        "new_string_char_array_fold"
653    }
654    fn kinds(&self) -> &'static [NodeKind] {
655        &[NodeKind::New]
656    }
657    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
658        let root = ctx.root();
659        let lang = ctx.lang;
660        let NodeData::New { ty, .. } = lang.data(id) else {
661            return None;
662        };
663        if !matches!(ty, JType::Ref(n) if n == "String" || n == "java.lang.String") {
664            return None;
665        }
666        let _ = root;
667        if lang.unit_shadows_lang || lang.unit_string_identity {
668            return None;
669        }
670        // 唯一实参:new char[]{...}(NewArray sized=0,唯一孩子 ArrayLit)
671        let args = lang.children(id).to_vec();
672        if args.len() != 1 {
673            return None;
674        }
675        let NodeData::NewArray { sized, .. } = lang.data(args[0]) else {
676            return None;
677        };
678        if *sized != 0 {
679            return None;
680        }
681        let arr_children = lang.children(args[0]).to_vec();
682        let lit = *arr_children.first()?;
683        if lang.kind(lit) != NodeKind::ArrayLit {
684            return None;
685        }
686        let mut text = String::new();
687        for &c in lang.children(lit) {
688            match lang.literal(c) {
689                Some(LitRef::Char(ch)) => text.push(ch),
690                _ => return None, // 非字符字面量(变量/表达式)不折
691            }
692        }
693        let with = lang.build_str(&text);
694        Some(Edit::Replace {
695            target: id,
696            with,
697        })
698    }
699}
700
701// ---------------------------------------------------------------------------
702// 空 finally 剥除(dex2jar/ProGuard 产物):
703//   try { B } finally {}(无 catch、无资源)→ B
704//   try { B } catch… finally {} → try { B } catch…(空 finally 删除)
705// 语义:空 finally 无可观察行为;有 catch 时保留 try/catch 结构。
706// ---------------------------------------------------------------------------
707
708pub struct EmptyFinallyStrip;
709
710impl Rule<JavaAst> for EmptyFinallyStrip {
711    fn name(&self) -> &'static str {
712        "empty_finally_strip"
713    }
714    fn kinds(&self) -> &'static [NodeKind] {
715        &[NodeKind::Try]
716    }
717    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
718        let lang = ctx.lang;
719        if lang.kind(id) != NodeKind::Try {
720            return None;
721        }
722        let ch = lang.children(id).to_vec();
723        // children: [resource…, try_block, catch…, (finally)?]
724        // 【关键】try_block = 第一个 Block 孩子;finally 只能是 try_block 与
725        // catch 之后的最后孩子。**try_block 本身绝不在此删**(空 try 块交给
726        // TryUnwrapNoCatch 整体解包)——曾经"last 为空块就删"会把空 try_block
727        // 误当 finally 剥掉,try-with-resources 的资源直接暴露在 try 头外
728        //(spoon sniperPrinter 真实语料抓获)。
729        let is_empty_block = |n: JavaId| -> bool {
730            lang.kind(n) == NodeKind::Block && lang.children(n).is_empty()
731        };
732        let try_idx = ch.iter().position(|&c| lang.kind(c) == NodeKind::Block)?;
733        if ch.len() < try_idx + 2 {
734            return None; // try_block 后没有孩子(无 finally)
735        }
736        // finally 存在性:最后孩子在 try_idx 之后且不是 catch
737        let last = *ch.last()?;
738        if lang.kind(last) == NodeKind::Catch {
739            return None;
740        }
741        if !is_empty_block(last) {
742            return None;
743        }
744        // last 距 try_idx 至少隔 1(try_idx 本身不可是 last——上面已保证
745        // ch.len() ≥ try_idx+2,但 last 也可能是紧随 try_block 的 catch 后的 finally)
746        Some(Edit::Splice {
747            node: id,
748            index: ch.len() - 1,
749            remove: 1,
750            insert: Vec::new(),
751        })
752    }
753}
754
755// ---------------------------------------------------------------------------
756// 无 catch + 无资源 + 空/无 finally → 完全解包 try:
757//   try { B } finally {} / try { B } → B
758// ---------------------------------------------------------------------------
759
760
761// ---------------------------------------------------------------------------
762// 纯重抛 catch 剥除(fernflower 异常表残迹——审查统计 ≥23 处/11 文件):
763//   try { S } catch (T v) { throw v; }   →  S            (唯一 catch)
764//   try { S } catch (A a) { throw a; } catch (B b) { …真处理… } →
765//   try { S } catch (B b) { …真处理… }                   (只删重抛臂)
766// 守卫:catch 体**只有**一条 throw 语句且抛的正是 catch 绑定变量自身
767//(变量在其他处无使用——单一语句保证);finally / 资源不动。
768// 语义:捕获后原样重抛对求值顺序、副作用、异常路径与堆栈完全透明
769//(栈深 +1 帧的差异源码层不可观察)。
770// ---------------------------------------------------------------------------
771
772
773// ---------------------------------------------------------------------------
774// 虚拟执行 V2(static_exec):对**根块**(方法体 / 静态初始化块 / 字段
775// init)的纯计算段整体求值,重写为「逃逸局部 = 常量」+「字段写 = 常量」
776// 的直线赋值。
777//
778// 目标形态(fernflower 语料 12/66 文件、残留噪声主体):静态字符串
779// 解密机——`"密文".toCharArray()` + `switch(i%5)` 常量键 + `(char)(c^k)`
780// 循环 + `new String(arr).intern()`。语义全部由 vexec 解释器保守
781// 求值(任何未知读/副作用/超预算立即放弃)。
782//
783// 正确性不变量:
784//   - 仅根块(walk 无父)——嵌套块的存储无法区分外层局部/字段;
785//   - 方法参数写 → 放弃(param 守卫);
786//   - 单路径确定性:条件/循环的条件全部求值为常量,执行走过的就是
787//     全部活路径;
788//   - 提前 Return/Break 只允许发生在**最后一条**顶层语句(此前返回
789//     则后续语句在别的路径可达,单路径值不构成证明);
790//   - 逃逸局部(后半块句法引用的段内局部)与字段写的值必须可
791//     材料化(Str/Int/Long/Bool/Str[]),否则放弃;
792//   - 值中含未配对代理(UTF-8 不可表示)→ 放弃。
793// ---------------------------------------------------------------------------
794
795
796/// 幂等守卫用的宽松等价:字面量按**值**等价(NumRaw{val:Int(0)} ≡
797/// Int(0)——打印相同),VarDecl 忽略类型标注差异(Int ≡ var)。
798/// static_exec 产物每轮被零打印差编辑重写(ASTParserTokenManager
799/// 抓获:ExprStmt→裸Assign/NumRaw→Int 交替),须在此归零。
800fn print_equiv(lang: &JavaAst, a: JavaId, b: JavaId) -> bool {
801    if lang.kind(a) != lang.kind(b) {
802        // ExprStmt{X} ≡ X(裸语句形态差异)
803        let (ia, ib) = (lang.kind(a), lang.kind(b));
804        let unwrap = |k: NodeKind, x: JavaId| -> Option<JavaId> {
805            if k == NodeKind::ExprStmt {
806                lang.children(x).first().copied()
807            } else {
808                Some(x)
809            }
810        };
811        return match (unwrap(ia, a), unwrap(ib, b)) {
812            (Some(x), Some(y)) => print_equiv_inner(lang, x, y),
813            _ => false,
814        };
815    }
816    print_equiv_inner(lang, a, b)
817}
818
819fn print_equiv_inner(lang: &JavaAst, a: JavaId, b: JavaId) -> bool {
820    if lang.kind(a) != lang.kind(b) {
821        return false;
822    }
823    // 字面量按值等价
824    match (lang.literal(a), lang.literal(b)) {
825        (Some(x), Some(y)) => {
826            if x != y {
827                return false;
828            }
829        }
830        (None, None) => {}
831        _ => return false,
832    }
833    let (ca, cb) = (lang.children(a), lang.children(b));
834    if ca.len() != cb.len() {
835        return false;
836    }
837    ca.iter().zip(cb.iter()).all(|(&x, &y)| print_equiv_inner(lang, x, y))
838}
839
840/// rest 段是否对裸名字段**再赋值**:Assign/复合 Assign 的目标为该名的
841/// VarRef,或 ++/-- 操作数。数组元素写(name[i]=v,Assign 目标是 Index)
842/// 改内容不改绑定 → 不算。
843fn rest_reassigns_field(lang: &JavaAst, rest: &[JavaId], name: &str) -> bool {
844    for &root in rest {
845        let mut stack = vec![root];
846        while let Some(n) = stack.pop() {
847            match lang.kind(n) {
848                NodeKind::Assign => {
849                    let t = lang.children(n)[0];
850                    if lang.kind(t) == NodeKind::VarRef && lang.var_name(t) == Some(name) {
851                        return true;
852                    }
853                }
854                NodeKind::Unary => {
855                    if let NodeData::Unary { op } = lang.data(n) {
856                        if op.is_incdec() {
857                            let t = lang.children(n)[0];
858                            if lang.kind(t) == NodeKind::VarRef
859                                && lang.var_name(t) == Some(name)
860                            {
861                                return true;
862                            }
863                        }
864                    }
865                }
866                _ => {}
867            }
868            for &c in lang.children(n) {
869                stack.push(c);
870            }
871        }
872    }
873    false
874}
875
876pub struct StaticExec;
877
878impl Rule<JavaAst> for StaticExec {
879    fn name(&self) -> &'static str {
880        "static_exec"
881    }
882    fn kinds(&self) -> &'static [NodeKind] {
883        &[NodeKind::Block]
884    }
885    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
886        // 仅根块:嵌套块的裸名存储无法区分外层局部与字段(下降遍历在
887        // vexec 内部自带作用域管理,规则侧只见根)
888        if ctx.parent(id).is_some() {
889            return None;
890        }
891        let lang = ctx.lang;
892        if lang.kind(id) != NodeKind::Block {
893            return None;
894        }
895        let stmts = lang.children(id).to_vec();
896        if stmts.len() < 3 {
897            return None; // 少于 3 条语句的段无折叠价值
898        }
899        // 前缀遍历(子块截断):逐语句执行,失败语句若是 Block/Label/Try
900        // 则下降进孩子继续;语句级失败前深快照、失败后回滚——遍历后的
901        // 状态只含已完成语句的效应(「半途倾倒」污染从根上消除,
902        // r/a4/a5/a3 的 AIOOBE/final 双赋值即此污染)。Class.forName
903        //(JDK 类)假设成功,作为不透明副作用进 effect_log 重放。
904        let mut ex = vexec::Exec::new(lang);
905        let run = ex.run_prefix(&stmts).ok()?;
906        if std::env::var("CURE_DBG_P1").is_ok() {
907            eprintln!(
908                "[P1-se] run ok: cuts={} total_completed={} budget={} log_len={} root_cut=({},{}) events={:?}",
909                run.cuts.len(),
910                run.cuts.iter().map(|c| c.completed).sum::<usize>(),
911                ex.budget_exhausted(),
912                ex.effect_log().len(),
913                run.cuts.last().map(|c| c.log_start).unwrap_or(0),
914                run.cuts.last().map(|c| c.log_end).unwrap_or(0),
915                ex.effect_log().iter().map(|e| match e {
916                    vexec::EffectEvent::FieldWrite(_) => "FW",
917                    vexec::EffectEvent::ClassLoad { .. } => "CL",
918                    vexec::EffectEvent::OpaqueFieldWrite { .. } => "OFW",
919                }).collect::<Vec<_>>()
920            );
921        }
922        // 步数预算耗尽:中途状态非收敛值——整段放弃(不回写任何常量)
923        if ex.budget_exhausted() {
924            return None;
925        }
926        let root_idx = run.root_cut_index();
927        // 总完成量门槛(根 + 嵌套层合计;嵌套截断单独也有价值)
928        let total_completed: usize = run.cuts.iter().map(|c| c.completed).sum();
929        if total_completed < 3 {
930            return None;
931        }
932        // ---- 截断守卫:前缀字段写(全日志)被**真实剩余**再赋值 → 放弃
933        // 整段(final 双重赋值不可编译)。仅读 → 放行(材料化写恰为读
934        // 供值)。真实剩余 = 各停止层剩余语句全集(含失败语句内部)。
935        let true_rest = run.true_rest();
936        if !true_rest.is_empty() {
937            for ev in ex.effect_log().iter() {
938                if let vexec::EffectEvent::FieldWrite(k) = ev {
939                    if let Some(name) = lang.name_of_key(*k) {
940                        // final 双重赋值不可编译 → 放弃整段;非 final 的
941                        // 材料化写被 rest 再赋值杀掉 = 死写,语义恒等
942                        //(a3/a4/a5:尾部 fernflower 复制分支再赋值非 final
943                        // 字段曾使整段放弃——全有或全无 → 放宽为部分材料化)
944                        if lang.final_fields.contains(&name)
945                            && rest_reassigns_field(lang, &true_rest, &name)
946                        {
947                            return None;
948                        }
949                    }
950                }
951            }
952        }
953        // ---- 提取数据(解除 &mut 借用)----
954        let log: Vec<vexec::EffectEvent> = ex.effect_log().to_vec();
955        let field_finals: Vec<(u32, vexec::VVal)> =
956            ex.field_writes().iter().map(|(k, v)| (*k, v.clone())).collect();
957        let decl_order: Vec<u32> = ex.decl_order().to_vec();
958        // Return 载荷是段输出的一部分:`int m(){…;return x;}` 的 x 必须
959        // 物化为 `return <常量>;`(CLI 测试抓获:方法体曾被吞成空体)
960        let ret_payload: Option<Option<vexec::VVal>> = match &run.early_exit {
961            Some(vexec::Flow::Return(v)) => Some(v.clone()),
962            _ => None,
963        };
964        // 每层逃逸局部:decl_order[decl_start .. 更深层 decl_start] 中
965        // 存活且名字被该层以下剩余引用者。值先取出。
966        let mut escaped_by_cut: Vec<Vec<(u32, vexec::VVal, u8, JType)>> = Vec::new();
967        for (j, cut) in run.cuts.iter().enumerate() {
968            let range_end = if j == 0 {
969                decl_order.len()
970            } else {
971                run.cuts[j - 1].decl_start
972            };
973            let rest_j = run.rest_for_cut(j);
974            let mut esc: Vec<(u32, vexec::VVal, u8, JType)> = Vec::new();
975            for &k in &decl_order[cut.decl_start.min(range_end)..range_end] {
976                if ex.var_value(k).is_none() {
977                    continue; // 已随作用域弹出——非存活局部
978                }
979                let name = lang.name_of_key(k)?;
980                let referenced = rest_j.iter().any(|&st| {
981                    cure_engine::analysis::subtree_contains(lang, st, |n| {
982                        lang.kind(n) == NodeKind::VarRef
983                            && lang.var_name(n) == Some(name.as_str())
984                    })
985                });
986                if referenced {
987                    esc.push((
988                        k,
989                        ex.var_value(k).cloned().unwrap_or(vexec::VVal::Undef),
990                        ex.var_domain(k),
991                        // 原声明类型:引用声明(装箱语义)材料化必须原样
992                        // 恢复——var 推断基本类型使接收位不可解引用
993                        ex.var_decl_ty(k).unwrap_or(JType::Var),
994                    ));
995                }
996            }
997            escaped_by_cut.push(esc);
998        }
999        drop(ex);
1000        // ---- 每层材料化(日志区间按执行序重放)----
1001        let mut edits: Vec<Edit<JavaAst>> = Vec::new();
1002        for (j, cut) in run.cuts.iter().enumerate() {
1003            let block_id = if j == root_idx {
1004                id
1005            } else {
1006                cut.block_hint?
1007            };
1008            let mut insert: Vec<JavaId> = Vec::new();
1009            // 日志区间 [log_start, log_end):
1010            for pos in cut.log_start..cut.log_end {
1011                match &log[pos] {
1012                    vexec::EffectEvent::FieldWrite(k) => {
1013                        // 仅该名字全日志最后一次写发语句(末值;中间写被杀)
1014                        let is_last = !log[pos + 1..]
1015                            .iter()
1016                            .any(|ev| matches!(ev, vexec::EffectEvent::FieldWrite(k2) if k2 == k));
1017                        if !is_last {
1018                            continue;
1019                        }
1020                        let name = lang.name_of_key(*k)?;
1021                        // 参数守卫(方法根:字段名撞参数名 → 放弃)
1022                        if lang.is_param_name(&name) {
1023                            return None;
1024                        }
1025                        let v = field_finals
1026                            .iter()
1027                            .find(|(fk, _)| fk == k)
1028                            .map(|(_, v)| v.clone())?;
1029                        // 字段声明域截断(静态块里 f += …L 的复合赋值
1030                        // 隐式收窄):static int/byte/short/char 字段上的
1031                        // L 值按域截断成 Int(t04i 抓获:f = 3000000001L
1032                        // 写进 static int 不可编译且值错)
1033                        let v = match (&v, lang.field_types.get(&name)) {
1034                            (vexec::VVal::L(x), Some(JType::Int)) => vexec::VVal::I(*x as i32),
1035                            (vexec::VVal::L(x), Some(JType::Byte)) => vexec::VVal::I(*x as i8 as i32),
1036                            (vexec::VVal::L(x), Some(JType::Short)) => vexec::VVal::I(*x as i16 as i32),
1037                            (vexec::VVal::L(x), Some(JType::Char)) => vexec::VVal::I(*x as u16 as i32),
1038                            _ => v,
1039                        };
1040                        let (val, mut extra) = materialize(lang, &v)?;
1041                        let tgt = lang.var(&name);
1042                        insert.push(lang.assign(tgt, val));
1043                        insert.append(&mut extra);
1044                    }
1045                    vexec::EffectEvent::OpaqueFieldWrite { k, stmt, call, args } => {
1046                        if std::env::var("CURE_DBG_P1").is_ok() {
1047                            eprintln!("[P1-mat] OpaqueFieldWrite entered k={}", k);
1048                        }
1049                        // 屏障重放:`name = <原调用子树,可求值实参代常量>`。
1050                        // 实参逐个求值——VVal::S/I/L/B → 字面量替换;
1051                        // 不可求值(Undef/数组)→ 保留原子树(语义不触碰)。
1052                        // 副作用 = 原调用在原位置执行——求值序保持
1053                        let name = lang.name_of_key(*k)?;
1054                        // 参数守卫(方法根:字段名撞参数名 → 放弃)
1055                        if lang.is_param_name(&name) {
1056                            return None;
1057                        }
1058                        let cch = lang.children(*call).to_vec();
1059                        let mut new_args: Vec<JavaId> = Vec::new();
1060                        for (i, &arg) in cch.iter().enumerate().skip(1) {
1061                            let val = args.get(i - 1).cloned().flatten();
1062                            let replaced = match val {
1063                                Some(vexec::VVal::S(t)) => Some(lang.lit(Lit::Str(t))),
1064                                Some(vexec::VVal::I(x)) => Some(lang.lit(Lit::Int(x as i64))),
1065                                Some(vexec::VVal::L(x)) => Some(lang.lit(Lit::Long(x))),
1066                                Some(vexec::VVal::B(b)) => Some(lang.lit(Lit::Bool(b))),
1067                                _ => None,
1068                            };
1069                            new_args.push(replaced.unwrap_or_else(|| lang.copy_subtree(arg)));
1070                        }
1071                        let callee = *lang.children(*call).first()?;
1072                        let callee_copy = lang.copy_subtree(callee);
1073                        let mut ch = vec![callee_copy];
1074                        ch.extend(new_args);
1075                        let new_call = lang.clone_node(*call, ch);
1076                        let new_stmt = if lang.kind(*stmt) == NodeKind::ExprStmt {
1077                            lang.clone_node(*stmt, vec![new_call])
1078                        } else {
1079                            new_call
1080                        };
1081                        // 赋值重放:name = <调用>
1082                        let tgt = lang.var(&name);
1083                        let assign = lang.assign(tgt, new_stmt);
1084                        insert.push(assign);
1085                    }
1086                    vexec::EffectEvent::ClassLoad { stmt, call, name } => {
1087                        // 重放为「try { 原语句(字面量实参) } catch (Exception
1088                        // cure$ex) {}」——forName 抛受检 ClassNotFoundException,
1089                        // 裸语句在 javac 下不可编译(终审代理抓获:修复输入
1090                        // 固有缺陷后输出独有「未报告的异常错误」——曾因 var121
1091                        // 前置错误掩蔽)。catch 在 JDK 假设下是死路,仅为
1092                        // 受检异常路径的编译合法性;形参名 cure$ex 防局部撞名
1093                        //(catch 形参不得遮蔽作用域内局部)
1094                        let lit = lang.lit(Lit::Str(name.clone()));
1095                        let callee = *lang.children(*call).first()?;
1096                        let callee_copy = lang.copy_subtree(callee);
1097                        let new_call = lang.clone_node(*call, vec![callee_copy, lit]);
1098                        let new_stmt = if lang.kind(*stmt) == NodeKind::ExprStmt {
1099                            lang.clone_node(*stmt, vec![new_call])
1100                        } else {
1101                            new_call
1102                        };
1103                        let try_block = lang.block(vec![new_stmt]);
1104                        let catch_block = lang.block(vec![]);
1105                        let catch_arm = lang.catch_("Exception", "cure$ex", catch_block);
1106                        let wrapped = lang.try_(vec![], try_block, vec![catch_arm], None);
1107                        insert.push(wrapped);
1108                    }
1109                }
1110            }
1111            // 逃逸局部(保持原声明序):值不可材料化 → 放弃整段重写。
1112            // 别名保持:同一数组(CA/SA 同 Rc)的多名只发一份字面量,其余
1113            // 以 `var x = <首名>` 声明——o.java 抓获:var17/var39 双别名
1114            // 被拆成独立数组,rest 的就地解码写错数组(正则被腐蚀)
1115            let mut alias_first: std::collections::HashMap<usize, String> =
1116                std::collections::HashMap::new();
1117            for (k, v, dom, dty) in &escaped_by_cut[j] {
1118                let name = lang.name_of_key(*k)?;
1119                let (val, mut extra) = match v {
1120                    vexec::VVal::CA(a) => {
1121                        let addr = std::rc::Rc::as_ptr(a) as usize;
1122                        match alias_first.get(&addr) {
1123                            Some(first) => (lang.var(first), vec![]),
1124                            None => {
1125                                alias_first.insert(addr, name.clone());
1126                                materialize(lang, v)?
1127                            }
1128                        }
1129                    }
1130                    vexec::VVal::SA(a) => {
1131                        let addr = std::rc::Rc::as_ptr(a) as usize;
1132                        match alias_first.get(&addr) {
1133                            Some(first) => (lang.var(first), vec![]),
1134                            None => {
1135                                alias_first.insert(addr, name.clone());
1136                                materialize(lang, v)?
1137                            }
1138                        }
1139                    }
1140                    _ => materialize(lang, v)?,
1141                };
1142                // 声明域恢复:JType::Var 丢失 char/byte/short/long 的
1143                // 静态类型(v13 抓获:char c='x' 物化成 var c=120,
1144                // valueOf(c) 折成 "120";long 同源折 i32)。char 域的
1145                // I 值发 Char 字面量;long 域的 I 值发 5L
1146                let (ty, val) = match (*dom, val) {
1147                    (2, lit) if lang.kind(lit) == NodeKind::Literal => {
1148                        // Int/Long 字面量都按 char 域转换(t04f 抓获:
1149                        // Long 直通产出 char c = 98L 不可编译)
1150                        match lang.literal(lit) {
1151                            Some(LitRef::Int(v)) => {
1152                                match u32::try_from(v).ok().and_then(char::from_u32) {
1153                                    Some(c) => (JType::Char, lang.lit(Lit::Char(c))),
1154                                    None => (JType::Char, lit),
1155                                }
1156                            }
1157                            Some(LitRef::Long(v)) => {
1158                                match u32::try_from(v as i32 as i64).ok().and_then(char::from_u32) {
1159                                    Some(c) => (JType::Char, lang.lit(Lit::Char(c))),
1160                                    None => (JType::Char, lit),
1161                                }
1162                            }
1163                            _ => (JType::Char, lit),
1164                        }
1165                    }
1166                    (3, lit) => (JType::Byte, lit),
1167                    (4, lit) => (JType::Short, lit),
1168                    (5, lit) if lang.kind(lit) == NodeKind::Literal => {
1169                        if let Some(LitRef::Int(v)) = lang.literal(lit) {
1170                            (JType::Long, lang.build_int(v, true))
1171                        } else {
1172                            (JType::Long, lit)
1173                        }
1174                    }
1175                    // 引用声明(Object/接口等)按原类型恢复(装箱在赋值
1176                    // 位合法——R14 族 A:`Object o = i + j` 曾发 var o = 3
1177                    // → o.hashCode() 不可解引用)。基本类型声明保持 var
1178                    //(与推断同型)
1179                    (_, lit) => {
1180                        if matches!(dty, JType::Ref(_)) {
1181                            (dty.clone(), lit)
1182                        } else {
1183                            (JType::Var, lit)
1184                        }
1185                    }
1186                };
1187                let stmt = lang.var_decl(&name, ty, Some(val));
1188                insert.push(stmt);
1189                insert.append(&mut extra);
1190            }
1191            // 根层 return 载荷(void 裸 return 不发——trailing_return 清理)
1192            if j == root_idx {
1193                if let Some(v) = ret_payload.clone().flatten() {
1194                    let (val, mut extra) = materialize(lang, &v)?;
1195                    let stmt = lang.ret(Some(val));
1196                    insert.push(stmt);
1197                    insert.append(&mut extra);
1198                }
1199            }
1200            // 无内容无删减的层跳过
1201            if insert.is_empty() && cut.completed == 0 {
1202                continue;
1203            }
1204            // 幂等守卫:该层产物与被删前缀逐位一致 → 跳过该层
1205            //(ASTParserTokenManager 曾每轮 1 次零差编辑——振荡误报)
1206            {
1207                let removed = lang.children(block_id).to_vec();
1208                if insert.len() == cut.completed
1209                    && cut.completed <= removed.len()
1210                    && insert
1211                        .iter()
1212                        .zip(removed[..cut.completed].iter())
1213                        .all(|(&n, &o)| print_equiv(lang, n, o))
1214                {
1215                    continue;
1216                }
1217            }
1218            edits.push(Edit::Splice {
1219                node: block_id,
1220                index: 0,
1221                remove: cut.completed,
1222                insert,
1223            });
1224        }
1225        if std::env::var("CURE_DBG_P1").is_ok() {
1226            eprintln!("[P1-se] edits={} (empty→reject)", edits.len());
1227        }
1228        if edits.is_empty() {
1229            return None;
1230        }
1231        // 纯死段(无输出无逃逸整段删除)等历史形态由空 insert splice 承载
1232        if edits.len() == 1 {
1233            Some(edits.pop().unwrap())
1234        } else {
1235            Some(Edit::Multi(edits))
1236        }
1237    }
1238}
1239
1240/// 值 → 常量字面量节点(不可材料化 → None)。
1241/// 值 → 常量表达式节点 + 追加语句(部分填充数组需 new T[n] + 分槽赋值)。
1242fn materialize(lang: &mut JavaAst, v: &vexec::VVal) -> Option<(JavaId, Vec<JavaId>)> {
1243    use vexec::VVal;
1244    match v {
1245        VVal::I(x) => Some((lang.lit(Lit::Int(*x as i64)), vec![])),
1246        VVal::L(x) => Some((lang.lit(Lit::Long(*x)), vec![])),
1247        VVal::B(b) => Some((lang.lit(Lit::Bool(*b)), vec![])),
1248        VVal::S(s) => Some((lang.lit(Lit::Str(s.clone())), vec![])),
1249        VVal::CA(a) => {
1250            // char[] 字面量:new char[]{...}
1251            let chars: Vec<char> = a.borrow().clone();
1252            let lits: Vec<JavaId> = chars.iter().map(|&c| lang.lit(Lit::Char(c))).collect();
1253            let lit_arr = lang.array_lit(lits);
1254            let node = lang.new_array(JType::Char, 1, 0, vec![], Some(lit_arr));
1255            Some((node, vec![]))
1256        }
1257        VVal::SA(elems) => {
1258            // String[]:全填充 → 字面量数组;部分填充 → new String[n] +
1259            // 逐元素赋值(null 槽保留——后半段继续填充)
1260            let arr = elems.borrow().clone();
1261            if arr.iter().all(|e| matches!(e, VVal::S(_))) {
1262                let lits: Vec<JavaId> = arr
1263                    .iter()
1264                    .map(|e| match e {
1265                        VVal::S(sv) => lang.lit(Lit::Str(sv.clone())),
1266                        _ => unreachable!(),
1267                    })
1268                    .collect();
1269                let lit_arr = lang.array_lit(lits);
1270                let node =
1271                    lang.new_array(JType::Ref("String".to_string()), 1, 0, vec![], Some(lit_arr));
1272                Some((node, vec![]))
1273            } else {
1274                // 部分填充(含 Undef 槽):元素静态类型未知——
1275                // new T[n] 的 T 无法从值推断(差分抓获:new int[5]
1276                // 曾被物化成 new String[5],类型损坏不可编译)。
1277                // 保守放弃(整段重写回退)
1278                None
1279            }
1280        }
1281        // Undef → 不可材料化(逃逸未初始化值可疑,放弃)
1282        VVal::Undef => None,
1283    }
1284}
1285
1286pub struct TryUnwrapRethrow;
1287
1288impl Rule<JavaAst> for TryUnwrapRethrow {
1289    fn name(&self) -> &'static str {
1290        "try_unwrap_rethrow"
1291    }
1292    fn kinds(&self) -> &'static [NodeKind] {
1293        &[NodeKind::Try]
1294    }
1295    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
1296        let lang = ctx.lang;
1297        if lang.kind(id) != NodeKind::Try {
1298            return None;
1299        }
1300        let ch = lang.children(id).to_vec();
1301        // 布局 [resource…, try_block, catch…, (finally)?]
1302        let try_block = *ch.iter().find(|&&c| lang.kind(c) == NodeKind::Block)?;
1303        let catches: Vec<JavaId> =
1304            ch.iter().copied().filter(|&c| lang.kind(c) == NodeKind::Catch).collect();
1305        if catches.is_empty() {
1306            return None;
1307        }
1308        let rethrow_only = |c: JavaId| -> bool {
1309            // Catch 布局 children: [block];绑定名在 NodeData::Catch
1310            let block = match lang.children(c).first() {
1311                Some(&b) => b,
1312                None => return false,
1313            };
1314            let stmts = lang.children(block);
1315            if stmts.len() != 1 {
1316                return false;
1317            }
1318            let stmt = stmts[0];
1319            if lang.kind(stmt) != NodeKind::Throw {
1320                return false;
1321            }
1322            let Some(&thrown) = lang.children(stmt).first() else { return false };
1323            // 抛的必须是 catch 绑定变量自身(整枝唯一使用 ⇒ 无别名逃逸)
1324            if lang.kind(thrown) != NodeKind::VarRef {
1325                return false;
1326            }
1327            match &lang.node(c).data {
1328                NodeData::Catch { name, .. } => lang.var_name(thrown) == Some(name.as_str()),
1329                _ => false,
1330            }
1331        };
1332        let all_rethrow = catches.iter().all(|&c| rethrow_only(c));
1333        if all_rethrow {
1334            // 无 finally / 资源(try_block 之外只允许这些 catch)→ 整体剥壳
1335            let extras: Vec<JavaId> = ch
1336                .iter()
1337                .copied()
1338                .filter(|&c| c != try_block && lang.kind(c) != NodeKind::Catch)
1339                .collect();
1340            if extras.is_empty() {
1341                return Some(Edit::Replace {
1342                    target: id,
1343                    with: try_block,
1344                });
1345            }
1346            return None;
1347        }
1348        // 混合:只删重抛臂(Splice 掉对应 catch 槽)。守卫:被删臂**之后**
1349        // 的所有 catch 也必须是纯重抛——否则该类型异常改道后续真处理臂
1350        //(JavaInputAstVisitor 抓获:删 catch(FormattingError){throw e} 后
1351        // Error 落进 catch(Throwable) 被重新包装,异常消息可观察改变)。
1352        // catch 匹配按序——更早的臂不受影响(它们本来就先匹配)。
1353        let catch_slots: Vec<usize> = ch
1354            .iter()
1355            .enumerate()
1356            .filter(|(_, &c)| lang.kind(c) == NodeKind::Catch)
1357            .map(|(i, _)| i)
1358            .collect();
1359        for &slot in &catch_slots {
1360            let c = ch[slot];
1361            if !rethrow_only(c) {
1362                continue;
1363            }
1364            let later_all_rethrow = catch_slots
1365                .iter()
1366                .filter(|&&s2| s2 > slot)
1367                .all(|&s2| rethrow_only(ch[s2]));
1368            if !later_all_rethrow {
1369                continue;
1370            }
1371            return Some(Edit::Splice {
1372                node: id,
1373                index: slot,
1374                remove: 1,
1375                insert: vec![],
1376            });
1377        }
1378        None
1379    }
1380}
1381
1382pub struct TryUnwrapNoCatch;
1383
1384impl Rule<JavaAst> for TryUnwrapNoCatch {
1385    fn name(&self) -> &'static str {
1386        "try_unwrap_no_catch"
1387    }
1388    fn kinds(&self) -> &'static [NodeKind] {
1389        &[NodeKind::Try]
1390    }
1391    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
1392        let lang = ctx.lang;
1393        if lang.kind(id) != NodeKind::Try {
1394            return None;
1395        }
1396        let ch = lang.children(id).to_vec();
1397        if ch.is_empty() {
1398            return None;
1399        }
1400        // 布局 [resource…, try_block, catch…, (finally)?]:
1401        // catch 节点识别(NodeData::Catch);资源是语句节点(VarDecl/ExprStmt)。
1402        let has_catch = ch.iter().any(|&c| lang.kind(c) == NodeKind::Catch);
1403        if has_catch {
1404            return None;
1405        }
1406        // 资源识别:try 块之前、非 try-block 的孩子数 —— 保守做法:
1407        // try_block = 第一个 Block 孩子(资源不会是 Block);其后的孩子
1408        // 只能是 finally(无 catch)。
1409        let try_block = *ch.iter().find(|&&c| lang.kind(c) == NodeKind::Block)?;
1410        let rest: Vec<JavaId> = ch
1411            .iter()
1412            .copied()
1413            .filter(|&c| c != try_block)
1414            .collect();
1415        // 其余(资源 + finally)必须全部为空 Block(即无 finally 或空 finally)
1416        // 且无资源语句。
1417        for &r in &rest {
1418            let is_empty_block = lang.kind(r) == NodeKind::Block && lang.children(r).is_empty();
1419            if !is_empty_block {
1420                return None; // 有资源或非空 finally
1421            }
1422        }
1423        Some(Edit::Replace {
1424            target: id,
1425            with: try_block,
1426        })
1427    }
1428}
1429
1430// ---------------------------------------------------------------------------
1431// 循环头断路还原(jadx/反编译器高频形态):
1432//   while (true) { if (c) { break; } REST }        →  while (!c) { REST }
1433//   while (true) { if (c) break; else { REST } … }  →  while (!c) { REST… }
1434// 安全性:c 在两种形态下都于每次迭代头部求值一次;break 无标签(目标是本循环)。
1435// ---------------------------------------------------------------------------
1436
1437pub struct LoopHeadBreak;
1438
1439impl Rule<JavaAst> for LoopHeadBreak {
1440    fn name(&self) -> &'static str {
1441        "loop_head_break"
1442    }
1443    fn kinds(&self) -> &'static [NodeKind] {
1444        &[NodeKind::While, NodeKind::DoWhile]
1445    }
1446    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
1447        let lang = ctx.lang;
1448        let body = match lang.kind(id) {
1449            NodeKind::While => {
1450                let ch = lang.children(id).to_vec();
1451                if !matches!(lang.literal(ch[0]), Some(LitRef::Bool(true))) {
1452                    return None;
1453                }
1454                ch[1]
1455            }
1456            NodeKind::DoWhile => {
1457                let ch = lang.children(id).to_vec();
1458                if !matches!(lang.literal(ch[1]), Some(LitRef::Bool(true))) {
1459                    return None;
1460                }
1461                ch[0]
1462            }
1463            _ => return None,
1464        };
1465        if lang.kind(body) != NodeKind::Block {
1466            return None;
1467        }
1468        let bch = lang.children(body).to_vec();
1469        let is_do = lang.kind(id) == NodeKind::DoWhile;
1470        // do-while 形态:if 必须是体里唯一语句
1471        if is_do && bch.len() != 1 {
1472            return None;
1473        }
1474        let first = *bch.first()?;
1475        if lang.kind(first) != NodeKind::If {
1476            return None;
1477        }
1478        let ich = lang.children(first).to_vec();
1479        let c = ich[0];
1480
1481        let bare_break = |n: JavaId| matches!(lang.data(n), NodeData::Break { label: None });
1482        let is_break = |n: JavaId| match lang.kind(n) {
1483            NodeKind::Break => bare_break(n),
1484            NodeKind::Block if lang.children(n).len() == 1 => bare_break(lang.children(n)[0]),
1485            _ => false,
1486        };
1487        let bare_continue = |n: JavaId| matches!(lang.data(n), NodeData::Continue { label: None });
1488        let ends_with_continue = |n: JavaId| -> bool {
1489            match lang.kind(n) {
1490                NodeKind::Continue => bare_continue(n),
1491                NodeKind::Block => lang
1492                    .children(n)
1493                    .last()
1494                    .is_some_and(|&l| lang.kind(l) == NodeKind::Continue && bare_continue(l)),
1495                _ => false,
1496            }
1497        };
1498        let strip_tail_continue = |n: JavaId| -> Vec<JavaId> {
1499            match lang.kind(n) {
1500                NodeKind::Continue => vec![],
1501                NodeKind::Block => {
1502                    let ch = lang.children(n).to_vec();
1503                    if ch
1504                        .last()
1505                        .is_some_and(|&l| lang.kind(l) == NodeKind::Continue && bare_continue(l))
1506                    {
1507                        ch[..ch.len() - 1].to_vec()
1508                    } else {
1509                        ch
1510                    }
1511                }
1512                _ => vec![n],
1513            }
1514        };
1515
1516        match ich.len() {
1517            2 => {
1518                // while (true) { if (c) { break; } REST } → while (!c) { REST }
1519                if is_do || !is_break(ich[1]) {
1520                    return None;
1521                }
1522                let mut rest: Vec<JavaId> = bch[1..].to_vec();
1523                if let Some(&last) = rest.last() {
1524                    if lang.kind(last) == NodeKind::Continue && bare_continue(last) {
1525                        rest.pop();
1526                    }
1527                }
1528                let nc = lang.build_unary(UnOp::Not, c);
1529                let inner = lang.build_block(rest);
1530                let w = lang.while_(nc, inner);
1531                Some(Edit::Replace {
1532                    target: id,
1533                    with: w,
1534                })
1535            }
1536            3 => {
1537                let (then, els) = (ich[1], ich[2]);
1538                // 形态 B/C:then 以 continue 结尾、else 是 break → while (c) { then' }
1539                if ends_with_continue(then) && is_break(els) {
1540                    let rest = strip_tail_continue(then);
1541                    let inner = lang.build_block(rest);
1542                    let w = lang.while_(c, inner);
1543                    return Some(Edit::Replace {
1544                        target: id,
1545                        with: w,
1546                    });
1547                }
1548                // 形态 D:then 是 break、else 以 continue 结尾 → while (!c) { else' }
1549                if is_break(then) && ends_with_continue(els) {
1550                    let rest = strip_tail_continue(els);
1551                    let inner = lang.build_block(rest);
1552                    let nc = lang.build_unary(UnOp::Not, c);
1553                    let w = lang.while_(nc, inner);
1554                    return Some(Edit::Replace {
1555                        target: id,
1556                        with: w,
1557                    });
1558                }
1559                // 既有形态:while (true) { if (c) break; else { REST } … }
1560                if !is_do && is_break(then) {
1561                    let mut all = strip_tail_continue(els);
1562                    all.extend(bch[1..].iter().copied());
1563                    let inner = lang.build_block(all);
1564                    let nc = lang.build_unary(UnOp::Not, c);
1565                    let w = lang.while_(nc, inner);
1566                    return Some(Edit::Replace {
1567                        target: id,
1568                        with: w,
1569                    });
1570                }
1571                None
1572            }
1573            _ => None,
1574        }
1575    }
1576}
1577
1578pub struct WhileIteratorToForEach;
1579
1580impl Rule<JavaAst> for WhileIteratorToForEach {
1581    fn name(&self) -> &'static str {
1582        "while_iterator_to_for_each"
1583    }
1584    fn kinds(&self) -> &'static [NodeKind] {
1585        &[NodeKind::While]
1586    }
1587    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
1588        // 先取结构信息(避免 lang 的可变借用与 ctx 方法冲突)
1589        let parent = ctx.parent(id)?;
1590        let idx = ctx.index(id)?;
1591        let lang = ctx.lang;
1592        if lang.kind(id) != NodeKind::While {
1593            return None;
1594        }
1595        let wch = lang.children(id).to_vec();
1596        let body = wch[1];
1597        // 前置声明:Iterator<…> it = <iterable>.iterator();
1598        if lang.kind(parent) != NodeKind::Block {
1599            return None;
1600        }
1601        if idx == 0 {
1602            return None;
1603        }
1604        let stmts = lang.children(parent).to_vec();
1605        // 向上扫描(最多 4 条)找 it 的 VarDecl(声明与 while 之间可能夹
1606        // 无关语句,如 acc 声明);中间语句引用 it 则拒
1607        let cond = wch[0];
1608        let it_hint = match lang.kind(cond) {
1609            NodeKind::Call => {
1610                let cc0 = lang.children(cond).to_vec();
1611                match lang.data(*cc0.first()?) {
1612                    NodeData::Member { name } if lang.sn(*name) == "hasNext" => {
1613                        lang.var_name(lang.children(cc0[0])[0]).map(|x| x.to_string())
1614                    }
1615                    _ => None,
1616                }
1617            }
1618            _ => None,
1619        };
1620        let mut decl = None;
1621        if let Some(hint) = it_hint {
1622            let mut up = idx;
1623            for _ in 0..4 {
1624                if up == 0 {
1625                    break;
1626                }
1627                up -= 1;
1628                let st = stmts[up];
1629                if lang.kind(st) == NodeKind::VarDecl && lang.var_name(st) == Some(hint.as_str()) {
1630                    decl = Some(st);
1631                    break;
1632                }
1633                if subtree_has_var(lang, st, &hint) {
1634                    break;
1635                }
1636            }
1637        }
1638        let decl = decl?;
1639        let it_name = lang.var_name(decl)?.to_string();
1640        let init_call = lang.children(decl).first().copied()?;
1641        if lang.kind(init_call) != NodeKind::Call {
1642            return None;
1643        }
1644        let ic = lang.children(init_call).to_vec();
1645        let NodeData::Member { name: m0 } = lang.data(*ic.first()?) else {
1646            return None;
1647        };
1648        if lang.sn(*m0) != "iterator" || ic.len() != 1 {
1649            return None;
1650        }
1651        let iterable = lang.children(ic[0])[0];
1652        if subtree_has_var(&*lang, iterable, &it_name) {
1653            return None;
1654        }
1655        // cond: it.hasNext()
1656        if lang.kind(cond) != NodeKind::Call {
1657            return None;
1658        }
1659        let cc = lang.children(cond).to_vec();
1660        let NodeData::Member { name: m1 } = lang.data(*cc.first()?) else {
1661            return None;
1662        };
1663        if lang.sn(*m1) != "hasNext" || cc.len() != 1 {
1664            return None;
1665        }
1666        if lang.var_name(lang.children(cc[0])[0]) != Some(it_name.as_str()) {
1667            return None;
1668        }
1669        // body: Block[ VarDecl e = it.next();, REST… ]
1670        if lang.kind(body) != NodeKind::Block {
1671            return None;
1672        }
1673        let bch = lang.children(body).to_vec();
1674        let first = *bch.first()?;
1675        if lang.kind(first) != NodeKind::VarDecl {
1676            // ===== 内联形态:body 中恰好一次 it.next()(可能 Cast/Paren 包裹)=====
1677            // it 在体内只能出现这一次(hasNext 已在 cond 消费)
1678            let it_refs_in_body: Vec<JavaId> = {
1679                let mut v = Vec::new();
1680                collect_var_refs(&*lang, body, &it_name, &mut v);
1681                v
1682            };
1683            if it_refs_in_body.len() != 1 {
1684                return None;
1685            }
1686            let next_recv = it_refs_in_body[0];
1687            // receiver 的父是 Member{next},Member 的父才是 Call(无实参)
1688            let member = parent_of_recv(lang, body, next_recv);
1689            if lang.kind(member) != NodeKind::Member {
1690                return None;
1691            }
1692            let NodeData::Member { name: mn } = lang.data(member) else {
1693                return None;
1694            };
1695            if lang.sn(*mn) != "next" {
1696                return None;
1697            }
1698            let next_call = parent_of_recv(lang, body, member);
1699            if lang.kind(next_call) != NodeKind::Call {
1700                return None;
1701            }
1702            if lang.children(next_call).len() != 1 {
1703                return None;
1704            }
1705            // 包裹节点(要被替换的):从 next_call 向上收集 Cast/Paren
1706            let mut wrapped = next_call;
1707            loop {
1708                let par = parent_of_recv(lang, body, wrapped);
1709                match lang.kind(par) {
1710                    NodeKind::Cast | NodeKind::Paren => wrapped = par,
1711                    _ => break,
1712                }
1713            }
1714            // 消费条件性守卫(第 12 轮攻击 A4a 抓获):next() 必须每轮
1715            // **无条件求值一次**。两类条件语境拒绝:
1716            //   a) 语句级:wrapped 所在语句嵌于 if/for/while/try/catch/
1717            //      switch/synchronized 内(消费次数或 0 或 N——A4a 内层
1718            //      for 消费 3 个/外层轮 → 3 9 变 9 27 实锤);
1719            //   b) 表达式级:三元分支位 / 短路 &&、|| 右操作数(求值可跳过)。
1720            // 顺序包裹(Block/Group/ExprStmt/Label/Return/Throw)与无条件
1721            // 求值位(Call/New 实参、非短路 Binary、Assign、Cast、Paren、
1722            // Index)放行
1723            {
1724                let mut cur = wrapped;
1725                while cur != body {
1726                    let par = parent_of_recv(lang, body, cur);
1727                    if par == body || par == cur {
1728                        break; // 直达 body(防御:不在子树内则停)
1729                    }
1730                    let ch = lang.children(par).to_vec();
1731                    let idx = ch.iter().position(|&c| c == cur);
1732                    match lang.kind(par) {
1733                        NodeKind::Block
1734                        | NodeKind::ExprStmt
1735                        | NodeKind::Label
1736                        | NodeKind::Return
1737                        | NodeKind::Throw => {}
1738                        NodeKind::Ternary => {
1739                            // 分支位(1/2)条件求值;条件位(0)无条件
1740                            if idx.unwrap_or(0) > 0 {
1741                                return None;
1742                            }
1743                        }
1744                        NodeKind::Binary => {
1745                            if let Some(op) = lang.bin_op(par) {
1746                                if matches!(op, BinOp::And | BinOp::Or) && idx == Some(1) {
1747                                    return None; // 短路右操作数
1748                                }
1749                            }
1750                        }
1751                        NodeKind::If
1752                        | NodeKind::While
1753                        | NodeKind::DoWhile
1754                        | NodeKind::For
1755                        | NodeKind::Switch
1756                        | NodeKind::Try
1757                        | NodeKind::Catch
1758                        | NodeKind::Synchronized
1759                        | NodeKind::ForEach => return None,
1760                        // Lambda 体:延迟求值(可能永不调用)——R13 T2d/
1761                        // T2f 抓获:() -> it.next() 传给从不调的方法曾被
1762                        // 折进 for-each 隐式消费(11→3);Assert:仅在断言
1763                        // 启用且到达时求值(默认禁用 = 0 消费)——T2e
1764                        NodeKind::Lambda | NodeKind::Assert => return None,
1765                        _ => {} // Call 实参/New 实参/Assign/Cast/Paren/Index…
1766                    }
1767                    cur = par;
1768                }
1769            }
1770            // 循环变量类型推导(三源优先级):
1771            // 1) **迭代器声明类型的泛型实参**(`Iterator<ByteArrayWrapper> it`
1772            //    → ByteArrayWrapper——最可靠:声明即约束,jedis JedisByteMap
1773            //    抓获:曾退化成 Object → e.data 编译错误,38 处/28 文件)
1774            // 2) 可迭代对象的元素类型(ArrayList<String>→String,裸类型→Object)
1775            // 元素为 Object 时【保留 Cast】(裸集合上 for (String e : raw) 非法,
1776            // 必须 for (Object e) + (String) e)
1777            let it_decl_elem = match lang.data(decl) {
1778                NodeData::VarDecl { ty, .. } => generic_elem_ty(ty),
1779                _ => None,
1780            };
1781            let elem = match it_decl_elem {
1782                Some(t) => t,
1783                None => match lang.var_type(iterable) {
1784                    Some(t) => generic_elem_ty(t).unwrap_or(JType::Ref("Object".into())),
1785                    None => JType::Ref("Object".into()),
1786                },
1787            };
1788            let elem = if for_each_elem_ok(&elem) {
1789                elem
1790            } else {
1791                JType::Ref("Object".into())
1792            };
1793            let is_object = matches!(&elem, JType::Ref(n) if n == "Object");
1794            let (replace_target, e_ty) = match lang.data(wrapped) {
1795                NodeData::Cast { .. } if is_object => (next_call, elem),
1796                NodeData::Cast { ty } => (wrapped, if is_object { ty.clone() } else { elem }),
1797                _ => (wrapped, elem),
1798            };
1799            // 新变量名(不与体内现有变量/catch 形参/for-each 绑定冲突——
1800            // A4c 抓获:catch (Exception e) 的形参不在 VarRef/VarDecl 域,
1801            // 撞名输出 javac「已定义变量 e」)
1802            let taken = |nm: &str| {
1803                subtree_has_var(&*lang, body, nm)
1804                    || subtree_has_binding(&*lang, body, nm)
1805            };
1806            let e_name = if taken("e") {
1807                if taken("e2") { "e3" } else { "e2" }
1808            } else {
1809                "e"
1810            };
1811            let e_ref = lang.var(e_name);
1812            let foreach = lang.for_each(e_name, e_ty, iterable, body);
1813            // 语句位置的裸 it.next();(ExprStmt 内容全体 = 该调用)——
1814            // 替换成 e; 不是合法语句(ConfigurationKeysLoader 抓获);
1815            // for-each 已隐式消费元素 → 整条 ExprStmt 删除
1816            let wrapped_parent = parent_of_recv(lang, body, wrapped);
1817            if lang.kind(wrapped_parent) == NodeKind::ExprStmt
1818                && *lang.children(wrapped_parent).first().unwrap_or(&wrapped) == wrapped
1819            {
1820                let stmt_block = parent_of_recv(lang, body, wrapped_parent);
1821                if let Some(si) = lang
1822                    .children(stmt_block)
1823                    .iter()
1824                    .position(|&c| c == wrapped_parent)
1825                {
1826                    return Some(Edit::Multi(vec![
1827                        Edit::Splice {
1828                            node: stmt_block,
1829                            index: si,
1830                            remove: 1,
1831                            insert: vec![],
1832                        },
1833                        Edit::Splice {
1834                            node: parent,
1835                            index: idx,
1836                            remove: 1,
1837                            insert: vec![foreach],
1838                        },
1839                        Edit::Delete { node: decl },
1840                    ]));
1841                }
1842            }
1843            return Some(Edit::Multi(vec![
1844                Edit::Replace {
1845                    target: replace_target,
1846                    with: e_ref,
1847                },
1848                Edit::Splice {
1849                    node: parent,
1850                    index: idx,
1851                    remove: 1,
1852                    insert: vec![foreach],
1853                },
1854                Edit::Delete { node: decl },
1855            ]));
1856        }
1857        let NodeData::VarDecl { name: e_name, ty, .. } = lang.data(first) else {
1858            return None;
1859        };
1860        let (e_name, ty) = (e_name.clone(), ty.clone());
1861        let next_call = lang.children(first).first().copied()?;
1862        let next_call = unwrap_paren_cast(&*lang, next_call);
1863        if lang.kind(next_call) != NodeKind::Call {
1864            return None;
1865        }
1866        let nc = lang.children(next_call).to_vec();
1867        let NodeData::Member { name: m2 } = lang.data(*nc.first()?) else {
1868            return None;
1869        };
1870        if lang.sn(*m2) != "next" || nc.len() != 1 {
1871            return None;
1872        }
1873        if lang.var_name(lang.children(nc[0])[0]) != Some(it_name.as_str()) {
1874            return None;
1875        }
1876        // 裸集合(元素 Object)上 for (更窄类型 e : raw) 非法 → 保守拒绝
1877        let elem_is_object = match lang.var_type(iterable) {
1878            Some(it_t) => generic_elem_ty(it_t).is_none(),
1879            None => true,
1880        };
1881        if elem_is_object && ty != JType::Ref("Object".into()) {
1882            return None;
1883        }
1884        // 通配符元素(Iterator<?> → for (? e : …) 非法)→ Object 兜底
1885        let ty = if for_each_elem_ok(&ty) { ty } else { JType::Ref("Object".into()) };
1886        let rest = bch[1..].to_vec();
1887        for &r in &rest {
1888            if subtree_has_var(&*lang, r, &it_name) {
1889                return None;
1890            }
1891        }
1892        let new_body = lang.build_block(rest);
1893        let foreach = lang.for_each(&e_name, ty, iterable, new_body);
1894        // 用 for-each 替换 while + 删除 it 声明(两处独立位置,
1895        // 顺序先 Splice(较大索引)后 Delete)
1896        Some(Edit::Multi(vec![
1897            Edit::Splice {
1898                node: parent,
1899                index: idx,
1900                remove: 1,
1901                insert: vec![foreach],
1902            },
1903            Edit::Delete { node: decl },
1904        ]))
1905    }
1906}
1907
1908// ---------------------------------------------------------------------------
1909// 拼接中的 String.valueOf 剥离(混淆器/反编译器包装):
1910//   String.valueOf(x) + y  →  x + y(y 为 String 字面量/已知 String 时)
1911//   y + String.valueOf(x)  →  y + x(同上)
1912// 安全性:valueOf 的字符串化语义与 + 拼接的隐式转换一致(null → "null");
1913// 守卫:另一侧必须可证为 String(保证 + 是拼接而非数值加法)。
1914// ---------------------------------------------------------------------------
1915
1916pub struct ConcatValueOfDrop;
1917
1918impl Rule<JavaAst> for ConcatValueOfDrop {
1919    fn name(&self) -> &'static str {
1920        "concat_value_of_drop"
1921    }
1922    fn kinds(&self) -> &'static [NodeKind] {
1923        &[NodeKind::Binary]
1924    }
1925    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
1926        let root = ctx.root();
1927        let lang = ctx.lang;
1928        if lang.kind(id) != NodeKind::Binary {
1929            return None;
1930        }
1931        if lang.bin_op(id) != Some(BinOp::Add) {
1932            return None;
1933        }
1934        let ch = lang.children(id).to_vec();
1935        let (l, r) = (ch[0], ch[1]);
1936        // 折叠后若成为常量表达式(双方均字面量),拼接串会被 javac 池化,
1937        // 而原 valueOf(...) 调用是运行期新建 → == 语义会变 → 身份守卫
1938        let becomes_constant = |x: JavaId, other: JavaId| {
1939            lang.literal(x).is_some() && matches!(lang.literal(other), Some(LitRef::Str(_)))
1940        };
1941        let is_value_of = |n: JavaId| -> Option<JavaId> {
1942            if lang.kind(n) != NodeKind::Call {
1943                return None;
1944            }
1945            let c = lang.children(n).to_vec();
1946            if c.len() != 2 {
1947                return None;
1948            }
1949            let callee = c[0];
1950            if let NodeData::Member { name } = lang.data(callee) {
1951                if lang.sn(*name) == "valueOf" && lang.var_name(lang.children(callee)[0]) == Some("String") {
1952                    return Some(c[1]);
1953                }
1954            }
1955            if let NodeData::VarRef { name } = lang.data(callee) {
1956                if lang.sn(*name) == "valueOf" {
1957                    return Some(c[1]);
1958                }
1959            }
1960            None
1961        };
1962        let stringy = |n: JavaId| -> bool {
1963            matches!(lang.literal(n), Some(LitRef::Str(_)))
1964                || lang
1965                    .var_type(n)
1966                    .is_some_and(|t| matches!(t, JType::Ref(n) if n == "String"))
1967        };
1968        // String.valueOf(x) + y(y 可证 String)
1969        if let Some(x) = is_value_of(l) {
1970            if stringy(r) {
1971                if becomes_constant(x, r) && lang.has_string_identity_compare(root) {
1972                    return None;
1973                }
1974                let with = lang.build_bin(BinOp::Add, x, r);
1975                return Some(Edit::Replace {
1976                    target: id,
1977                    with,
1978                });
1979            }
1980        }
1981        // y + String.valueOf(x)
1982        if let Some(x) = is_value_of(r) {
1983            if stringy(l) {
1984                if becomes_constant(x, l) && lang.has_string_identity_compare(root) {
1985                    return None;
1986                }
1987                let with = lang.build_bin(BinOp::Add, l, x);
1988                return Some(Edit::Replace {
1989                    target: id,
1990                    with,
1991                });
1992            }
1993        }
1994        None
1995    }
1996}
1997
1998
1999// ---------------------------------------------------------------------------
2000// 语句级 StringBuilder 链还原(ddc/jcdc 经典产物):
2001//   StringBuilder v0 = new StringBuilder().append(a0);
2002//   v0 = v0.append(a1);              // 重赋值形态
2003//   StringBuilder v1 = v0.append(a2); // 新变量形态
2004//   String s = v1.toString();
2005//   →  String s = <a0 + a1 + a2>;
2006// 守卫:链上每个变量的读次数恰为 1(仅作下一步 receiver),
2007// 首参非 String 时前置 ""(与表达式版一致);容量构造不折叠。
2008// 语义:各实参按序求值一次,两种形态一致。
2009// ---------------------------------------------------------------------------
2010
2011pub struct StringBuilderStatements;
2012
2013fn count_var_uses_in(lang: &JavaAst, root: JavaId, name: &str) -> usize {
2014    // 语义上是【读】的 VarRef 计数:简单赋值的 target 是纯写(不计);
2015    // 复合赋值(x += 1)与 inc/dec 的 target 是读+写(计)。
2016    let mut n = 0;
2017    let mut stack = vec![root];
2018    while let Some(x) = stack.pop() {
2019        if lang.kind(x) == NodeKind::Assign && lang.assign_op(x).is_none() {
2020            let ch = lang.children(x).to_vec();
2021            if let Some(&t) = ch.first() {
2022                let is_named_target =
2023                    lang.kind(t) == NodeKind::VarRef && lang.var_name(t) == Some(name);
2024                if !is_named_target {
2025                    stack.push(t);
2026                }
2027                for &c in &ch[1..] {
2028                    stack.push(c);
2029                }
2030            }
2031            continue;
2032        }
2033        if lang.kind(x) == NodeKind::VarRef && lang.var_name(x) == Some(name) {
2034            n += 1;
2035        }
2036        for &c in lang.children(x) {
2037            stack.push(c);
2038        }
2039    }
2040    n
2041}
2042
2043impl Rule<JavaAst> for StringBuilderStatements {
2044    fn name(&self) -> &'static str {
2045        "string_builder_statements"
2046    }
2047    fn kinds(&self) -> &'static [NodeKind] {
2048        &[NodeKind::VarDecl]
2049    }
2050    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
2051        // 取结构信息(避免与 ctx 方法借用冲突)
2052        let parent = ctx.parent(id)?;
2053        let idx = ctx.index(id)?;
2054        let root = ctx.root();
2055        let lang = ctx.lang;
2056
2057        if lang.kind(id) != NodeKind::VarDecl {
2058            return None;
2059        }
2060        if lang.kind(parent) != NodeKind::Block {
2061            return None;
2062        }
2063        let v0 = lang.var_name(id)?.to_string();
2064        // init: new SB().append(a0)
2065        let init = *lang.children(id).first()?;
2066        if lang.kind(init) != NodeKind::Call {
2067            return None;
2068        }
2069        let ic = lang.children(init).to_vec();
2070        if ic.len() != 2 {
2071            return None;
2072        }
2073        let NodeData::Member { name: m0 } = lang.data(ic[0]) else {
2074            return None;
2075        };
2076        if lang.sn(*m0) != "append" {
2077            return None;
2078        }
2079        let sb_new = lang.children(ic[0])[0];
2080        let (ctor_arg, a0) = match lang.data(sb_new) {
2081            NodeData::New { ty, .. } => {
2082                let is_sb = !lang.unit_shadows_lang
2083                && matches!(ty, JType::Ref(n) if n == "StringBuilder" || n == "java.lang.StringBuilder");
2084                if !is_sb {
2085                    return None;
2086                }
2087                let args = lang.children(sb_new).to_vec();
2088                match args.len() {
2089                    0 => (None, ic[1]),
2090                    1 => {
2091                        let a = args[0];
2092                        let stringy = matches!(lang.literal(a), Some(LitRef::Str(_)))
2093                            || lang
2094                                .var_type(a)
2095                                .is_some_and(|t| matches!(t, JType::Ref(n) if n == "String"));
2096                        if stringy {
2097                            (Some(a), ic[1])
2098                        } else if matches!(
2099                            lang.literal(a),
2100                            Some(LitRef::Int(_) | LitRef::Long(_))
2101                        ) {
2102                            // int/long 字面量必为容量构造——无语义差异
2103                            (None, ic[1])
2104                        } else {
2105                            return None; // 变量实参类型不可证
2106                        }
2107                    }
2108                    _ => return None,
2109                }
2110            }
2111            _ => return None,
2112        };
2113
2114        let stmts = lang.children(parent).to_vec();
2115        // 沿链前进:收集 append 实参与链语句
2116        let mut parts: Vec<JavaId> = vec![a0];
2117        let mut expected_reads: std::collections::HashMap<String, usize> =
2118            std::collections::HashMap::new();
2119        let mut chain_len = 1usize; // 已消费 v0 的 decl
2120        let mut cur = v0.clone();
2121        while idx + chain_len < stmts.len() {
2122            let s = stmts[idx + chain_len];
2123            let (recv, arg, new_var, consumed) = match lang.data(s) {
2124                // v = v.append(arg)(重赋值;ExprStmt 包裹或裸)
2125                NodeData::Assign { op: None } => {
2126                    let ch = lang.children(s).to_vec();
2127                    if ch.len() != 2 {
2128                        break;
2129                    }
2130                    let (t, v) = (ch[0], ch[1]);
2131                    if lang.kind(t) != NodeKind::VarRef
2132                        || lang.var_name(t) != Some(cur.as_str())
2133                        || lang.kind(v) != NodeKind::Call
2134                    {
2135                        break;
2136                    }
2137                    let vc = lang.children(v).to_vec();
2138                    if vc.len() != 2 {
2139                        break;
2140                    }
2141                    let NodeData::Member { name: mn } = lang.data(vc[0]) else {
2142                        break;
2143                    };
2144                    if lang.sn(*mn) != "append" {
2145                        break;
2146                    }
2147                    let recv = lang.children(vc[0])[0];
2148                    (recv, vc[1], false, s)
2149                }
2150                NodeData::ExprStmt => {
2151                    let ch = lang.children(s).to_vec();
2152                    if ch.len() != 1 {
2153                        break;
2154                    }
2155                    let inner = ch[0];
2156                    if !matches!(lang.data(inner), NodeData::Assign { op: None }) {
2157                        break;
2158                    }
2159                    let ach = lang.children(inner).to_vec();
2160                    if ach.len() != 2 {
2161                        break;
2162                    }
2163                    let (t, v) = (ach[0], ach[1]);
2164                    if lang.kind(t) != NodeKind::VarRef
2165                        || lang.var_name(t) != Some(cur.as_str())
2166                        || lang.kind(v) != NodeKind::Call
2167                    {
2168                        break;
2169                    }
2170                    let vc = lang.children(v).to_vec();
2171                    if vc.len() != 2 {
2172                        break;
2173                    }
2174                    let NodeData::Member { name: mn } = lang.data(vc[0]) else {
2175                        break;
2176                    };
2177                    if lang.sn(*mn) != "append" {
2178                        break;
2179                    }
2180                    let recv = lang.children(vc[0])[0];
2181                    (recv, vc[1], false, s)
2182                }
2183                // StringBuilder v2 = v.append(arg)(新变量)
2184                NodeData::VarDecl { .. } => {
2185                    let init = match lang.children(s).first() {
2186                        Some(&x) => x,
2187                        None => break,
2188                    };
2189                    if lang.kind(init) != NodeKind::Call {
2190                        break;
2191                    }
2192                    let vc = lang.children(init).to_vec();
2193                    if vc.len() != 2 {
2194                        break;
2195                    }
2196                    let NodeData::Member { name: mn } = lang.data(vc[0]) else {
2197                        break;
2198                    };
2199                    if lang.sn(*mn) != "append" {
2200                        break;
2201                    }
2202                    let recv = lang.children(vc[0])[0];
2203                    if lang.var_name(recv) != Some(cur.as_str()) {
2204                        break;
2205                    }
2206                    (recv, vc[1], true, s)
2207                }
2208                _ => break,
2209            };
2210            // receiver 必须是当前变量
2211            if lang.var_name(recv) != Some(cur.as_str()) {
2212                break;
2213            }
2214            parts.push(arg);
2215            *expected_reads.entry(cur.clone()).or_insert(0) += 1;
2216            if new_var {
2217                cur = lang.var_name(consumed)?.to_string();
2218            }
2219            chain_len += 1;
2220            let _ = consumed;
2221        }
2222
2223        // 链后必须紧跟:String s = <cur>.toString();
2224        let final_stmt = *stmts.get(idx + chain_len)?;
2225        let NodeData::VarDecl { .. } = lang.data(final_stmt) else {
2226            return None;
2227        };
2228        let s_init = *lang.children(final_stmt).first()?;
2229        if lang.kind(s_init) != NodeKind::Call {
2230            return None;
2231        }
2232        let sc = lang.children(s_init).to_vec();
2233        if sc.len() != 1 {
2234            return None;
2235        }
2236        let NodeData::Member { name: mn } = lang.data(sc[0]) else {
2237            return None;
2238        };
2239        if lang.sn(*mn) != "toString" {
2240            return None;
2241        }
2242        if lang.var_name(lang.children(sc[0])[0]) != Some(cur.as_str()) {
2243            return None;
2244        }
2245
2246        // 读次数守卫:实际读数 == 链内预期读数(receiver 次数 + toString 1 次),
2247        // 差值即存在链外使用 → 拒绝
2248        *expected_reads.entry(cur.clone()).or_insert(0) += 1; // toString receiver
2249        for (v, exp) in &expected_reads {
2250            if count_var_uses_in(&*lang, parent, v) != *exp {
2251                return None;
2252            }
2253        }
2254
2255        // 构造拼接表达式(全字面量链会折叠成常量表达式 → 池化 → 身份守卫)
2256        let mut all_parts: Vec<JavaId> = Vec::new();
2257        if let Some(ca) = ctor_arg {
2258            all_parts.push(ca);
2259        }
2260        all_parts.extend(parts);
2261        if all_parts.iter().all(|&p| lang.literal(p).is_some())
2262            && lang.has_string_identity_compare(root)
2263        {
2264            return None;
2265        }
2266        if all_parts.is_empty() {
2267            let empty = lang.build_str("");
2268            return Some(Edit::Multi(vec![
2269                Edit::Replace {
2270                    target: s_init,
2271                    with: empty,
2272                },
2273                Edit::Splice {
2274                    node: parent,
2275                    index: idx,
2276                    remove: chain_len,
2277                    insert: Vec::new(),
2278                },
2279            ]));
2280        }
2281        let first = all_parts[0];
2282        let stringy_first = matches!(lang.literal(first), Some(LitRef::Str(_)))
2283            || lang
2284                .var_type(first)
2285                .is_some_and(|t| matches!(t, JType::Ref(n) if n == "String"));
2286        let mut acc = if stringy_first {
2287            first
2288        } else {
2289            let empty = lang.build_str("");
2290            lang.build_bin(BinOp::Add, empty, first)
2291        };
2292        for &p in &all_parts[1..] {
2293            acc = lang.build_bin(BinOp::Add, acc, p);
2294        }
2295        Some(Edit::Multi(vec![
2296            Edit::Replace {
2297                target: s_init,
2298                with: acc,
2299            },
2300            Edit::Splice {
2301                node: parent,
2302                index: idx,
2303                remove: chain_len,
2304                insert: Vec::new(),
2305            },
2306        ]))
2307    }
2308}
2309
2310
2311// ---------------------------------------------------------------------------
2312// 尾部 continue 删除(标签感知版,从引擎移入):
2313//   循环体最后一条 continue:无标签,或标签即本循环 → 与落入下一轮等价,删除。
2314//   标签指向外层循环 → 语义不同,保留。
2315// ---------------------------------------------------------------------------
2316
2317pub struct TrailingContinueJava;
2318
2319impl Rule<JavaAst> for TrailingContinueJava {
2320    fn name(&self) -> &'static str {
2321        "trailing_continue"
2322    }
2323    fn kinds(&self) -> &'static [NodeKind] {
2324        &[NodeKind::While, NodeKind::For, NodeKind::DoWhile, NodeKind::ForEach]
2325    }
2326    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
2327        let parent = ctx.parent(id)?;
2328        let lang = ctx.lang;
2329        let body = match lang.kind(id) {
2330            NodeKind::While | NodeKind::For | NodeKind::DoWhile | NodeKind::ForEach => {
2331                *lang.children(id).last()?
2332            }
2333            _ => return None,
2334        };
2335        if lang.kind(body) != NodeKind::Block {
2336            return None;
2337        }
2338        let ch = lang.children(body).to_vec();
2339        let last = *ch.last()?;
2340        if lang.kind(last) != NodeKind::Continue {
2341            return None;
2342        }
2343        let label = match lang.data(last) {
2344            NodeData::Continue { label } => label.clone(),
2345            _ => return None,
2346        };
2347        match label {
2348            None => Some(Edit::Delete { node: last }),
2349            Some(l) => {
2350                // 标签必须命名本循环:父链上是 Label{l} 包着本循环
2351                if lang.kind(parent) == NodeKind::Label {
2352                    if let NodeData::Label { name } = lang.data(parent) {
2353                        if *name == l {
2354                            return Some(Edit::Delete { node: last });
2355                        }
2356                    }
2357                }
2358                None
2359            }
2360        }
2361    }
2362}
2363
2364
2365// ---------------------------------------------------------------------------
2366// 异或噪声消除(Java 语义版):Java 中 ^ 的操作数必为整型,
2367// 故无需 is_exact_int 类型证明即可折叠——覆盖含副作用/未知类型的场景:
2368//   (mark(5) ^ 0x5A) ^ 0x5A → mark(5)(调用保留、位置不变)
2369//   x ^ 0 / 0 ^ x → x
2370// ---------------------------------------------------------------------------
2371
2372pub struct XorNoise;
2373
2374impl Rule<JavaAst> for XorNoise {
2375    fn name(&self) -> &'static str {
2376        "xor_noise"
2377    }
2378    fn kinds(&self) -> &'static [NodeKind] {
2379        &[NodeKind::Binary]
2380    }
2381    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
2382        let lang = ctx.lang;
2383        if lang.kind(id) != NodeKind::Binary {
2384            return None;
2385        }
2386        let op = lang.bin_op(id)?;
2387        if op != BinOp::BitXor {
2388            return None;
2389        }
2390        let ch = lang.children(id).to_vec();
2391        let (l, r) = (ch[0], ch[1]);
2392        // x ^ 0 / 0 ^ x → x(x 任意:求值位置与次数都不变)
2393        let int_lit = |n: JavaId| lang.literal(n).and_then(|x| x.as_int());
2394        if int_lit(r) == Some(0) {
2395            return Some(Edit::Replace {
2396                target: id,
2397                with: l,
2398            });
2399        }
2400        if int_lit(l) == Some(0) {
2401            return Some(Edit::Replace {
2402                target: id,
2403                with: r,
2404            });
2405        }
2406        // (x ^ K1) ^ K2 → x ^ (K1^K2);K1^K2 == 0 → x(x 任意,保留原位)
2407        if lang.kind(l) != NodeKind::Binary || lang.bin_op(l) != Some(BinOp::BitXor) {
2408            return None;
2409        }
2410        let ich = lang.children(l).to_vec();
2411        let k1 = lang.literal(ich[1]).and_then(|x| x.as_int())?;
2412        let k2 = int_lit(r)?;
2413        let x = ich[0];
2414        let k = k1 ^ k2;
2415        let with = if k == 0 {
2416            x
2417        } else {
2418            let lit = lang.build_int(k, false);
2419            lang.build_bin(BinOp::BitXor, x, lit)
2420        };
2421        Some(Edit::Replace {
2422            target: id,
2423            with,
2424        })
2425    }
2426}
2427
2428// ---------------------------------------------------------------------------
2429// 字面量 length 折叠:"abc".length() → 3(字符串字面量长度编译期已知)。
2430// ---------------------------------------------------------------------------
2431
2432pub struct StrLenFold;
2433
2434impl Rule<JavaAst> for StrLenFold {
2435    fn name(&self) -> &'static str {
2436        "str_len_fold"
2437    }
2438    fn kinds(&self) -> &'static [NodeKind] {
2439        &[NodeKind::Call]
2440    }
2441    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
2442        // 语句位标志先取(ctx.lang 借用后 ctx 部分移动——R13 教训)
2443        let _at_stmt_pos = ctx
2444            .parent(id)
2445            .map(|p| ctx.lang.kind(p) == NodeKind::ExprStmt)
2446            .unwrap_or(false);
2447        let lang = ctx.lang;
2448        if lang.kind(id) != NodeKind::Call {
2449            return None;
2450        }
2451        let ch = lang.children(id).to_vec();
2452        if ch.len() != 1 {
2453            return None;
2454        }
2455        let NodeData::Member { name } = lang.data(ch[0]) else {
2456            return None;
2457        };
2458        if lang.sn(*name) != "length" {
2459            return None;
2460        }
2461        let recv = lang.children(ch[0])[0];
2462        if let Some(LitRef::Str(s)) = lang.literal(recv) {
2463            let with = lang.build_int(s.chars().count() as i64, false);
2464            return Some(Edit::Replace {
2465                target: id,
2466                with,
2467            });
2468        }
2469        None
2470    }
2471}
2472
2473
2474// ---------------------------------------------------------------------------
2475// 部分求值器("虚拟执行"):纯 JDK 方法 + 全字面量实参 → 编译期求值。
2476// 只在**求值成功**时折叠(失败/越界/异常路径保持原样交给运行时)。
2477// 覆盖混淆器常用的字符串藏匿手段:
2478//   "HelloWorld".substring(0,5) / .indexOf / .replace / .trim / .startsWith…
2479//   Integer.parseInt("42") / String.format("%s=%d",…) / String.valueOf
2480//   Math.abs/max/min / Character.isXxx / (char)('a'+2)
2481//   {"a","b"}[1](字面量数组下标) / Base64 解码链 new String(decoder.decode("…"))
2482// 语义安全边界:不折 toUpperCase/toLowerCase(locale 敏感)、
2483// parseInt 解析失败不折、substring 越界不折。
2484// ---------------------------------------------------------------------------
2485
2486pub struct LiteralEval;
2487
2488impl Rule<JavaAst> for LiteralEval {
2489    fn name(&self) -> &'static str {
2490        "literal_eval"
2491    }
2492    fn kinds(&self) -> &'static [NodeKind] {
2493        &[NodeKind::Call, NodeKind::Index, NodeKind::Cast]
2494    }
2495    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
2496        let root = ctx.root();
2497        let lang = ctx.lang;
2498        let edit = match lang.kind(id) {
2499            NodeKind::Call => self.eval_call(&mut *lang, id),
2500            NodeKind::Index => self.eval_index(&mut *lang, id),
2501            NodeKind::Cast => eval_cast_literal(&mut *lang, id),
2502            _ => None,
2503        };
2504        // Str 字面量结果:原表达式(valueOf/toString/concat/substring…)是运行期
2505        // 新建的串,折成字面量会引入池化引用;若方法内存在 String 的 ==/!=,
2506        // 身份语义可能改变 → 守卫。Bool/Int 结果走值语义,不受影响。
2507        if let Some(Edit::Replace { with, .. }) = &edit {
2508            if matches!(lang.literal(*with), Some(LitRef::Str(_)))
2509                && lang.has_string_identity_compare(root)
2510            {
2511                if std::env::var("CURE_DEBUG_GATE").is_ok() {
2512                    eprintln!("[gate] blocked literal_eval at {id:?}");
2513                }
2514                return None;
2515            }
2516        }
2517        edit
2518    }
2519}
2520
2521impl LiteralEval {
2522    fn eval_call(&self, lang: &mut JavaAst, id: JavaId) -> Option<Edit<JavaAst>> {
2523        let ch = lang.children(id).to_vec();
2524        let callee = ch[0];
2525        let NodeData::Member { name: method } = lang.data(callee) else {
2526            return None;
2527        };
2528        let method = *method;
2529        let recv = lang.children(callee)[0];
2530        // 实参必须全部为字面量
2531        let args: Vec<Lit> = ch[1..]
2532            .iter()
2533            .map(|&a| litref_to_lit(lang.literal(a)))
2534            .collect::<Option<Vec<_>>>()?;
2535
2536        let result: Option<Lit> = if let Some(LitRef::Str(s)) = lang.literal(recv) {
2537            eval_str_method(s, lang.sn(method), &args)
2538        } else if let Some(cls) = lang.var_name(recv) {
2539            eval_static_method(cls, lang.sn(method), &args)
2540        } else {
2541            None
2542        };
2543        let lit = result?;
2544        let with = lit_to_node(lang, &lit);
2545        Some(Edit::Replace {
2546            target: id,
2547            with,
2548        })
2549    }
2550
2551    fn eval_index(&self, lang: &mut JavaAst, id: JavaId) -> Option<Edit<JavaAst>> {
2552        // {"a","b"}[1] / new String[]{"a","b"}[1] → "b"(字面量数组 + 字面量下标,界内)
2553        let ch = lang.children(id).to_vec();
2554        // new T[]{…}(解析器归一化形态,sized=0 时唯一子节点是 ArrayLit)
2555        let arr = match lang.kind(ch[0]) {
2556            NodeKind::ArrayLit => ch[0],
2557            NodeKind::NewArray => {
2558                let nc = lang.children(ch[0]);
2559                let NodeData::NewArray { sized, .. } = lang.data(ch[0]) else {
2560                    return None;
2561                };
2562                if *sized != 0 || nc.len() != 1 || lang.kind(nc[0]) != NodeKind::ArrayLit {
2563                    return None;
2564                }
2565                nc[0]
2566            }
2567            _ => return None,
2568        };
2569        let idx = lang.literal(ch[1])?.as_int()?;
2570        let elems = lang.children(arr).to_vec();
2571        if idx < 0 || idx >= elems.len() as i64 {
2572            return None;
2573        }
2574        Some(Edit::Replace {
2575            target: id,
2576            with: elems[idx as usize],
2577        })
2578    }
2579}
2580
2581fn litref_to_lit(l: Option<LitRef<'_>>) -> Option<Lit> {
2582    Some(match l? {
2583        LitRef::Str(s) => Lit::Str(s.to_string()),
2584        LitRef::Int(v) => Lit::Int(v),
2585        LitRef::Long(v) => Lit::Long(v),
2586        LitRef::Bool(b) => Lit::Bool(b),
2587        LitRef::Char(c) => Lit::Char(c),
2588        LitRef::Float(v) => Lit::Float(v),
2589        LitRef::Double(v) => Lit::Double(v),
2590        LitRef::Null => return None,
2591    })
2592}
2593
2594fn lit_to_node(lang: &mut JavaAst, lit: &Lit) -> JavaId {
2595    match lit {
2596        Lit::Str(s) => lang.build_str(s),
2597        Lit::Int(v) => lang.build_int(*v, false),
2598        Lit::Long(v) => lang.build_int(*v, true),
2599        Lit::Bool(b) => lang.build_bool(*b),
2600        Lit::Char(c) => lang.build_char(*c),
2601        Lit::Float(v) => lang.lit(Lit::Float(*v)),
2602        Lit::Double(v) => lang.lit(Lit::Double(*v)),
2603        _ => lang.lit(Lit::Null),
2604    }
2605}
2606
2607/// 字面量 cast 折叠:(char)('a'+1) → 'b、(int)'a' → 97、窄化仅在值域内。
2608fn eval_cast_literal(lang: &mut JavaAst, id: JavaId) -> Option<Edit<JavaAst>> {
2609    let NodeData::Cast { ty } = lang.data(id) else {
2610        return None;
2611    };
2612    let ty = ty.clone();
2613    let inner = lang.children(id)[0];
2614    let lit = litref_to_lit(lang.literal(inner))?;
2615    let with = match (&ty, &lit) {
2616        (JType::Char, Lit::Int(v)) if (0_i64..=0xFFFF).contains(v) => {
2617            lang.build_char(char::from_u32(*v as u32)?)
2618        }
2619        (JType::Char, Lit::Char(_)) => inner,
2620        (JType::Int, Lit::Char(c)) => lang.build_int(*c as i64, false),
2621        (JType::Long, Lit::Char(c)) => lang.build_int(*c as i64, true),
2622        (JType::Long, Lit::Int(v)) => lang.build_int(*v, true),
2623        (JType::Int, Lit::Long(v)) if *v >= i32::MIN as i64 && *v <= i32::MAX as i64 => {
2624            lang.build_int(*v, false)
2625        }
2626        (JType::Int, Lit::Int(_)) => inner,
2627        _ => return None,
2628    };
2629    Some(Edit::Replace {
2630        target: id,
2631        with,
2632    })
2633}
2634
2635fn eval_str_method(s: &str, method: &str, args: &[Lit]) -> Option<Lit> {
2636    let chars: Vec<char> = s.chars().collect();
2637    let n = chars.len() as i64;
2638    Some(match (method, args) {
2639        ("isEmpty", []) => Lit::Bool(s.is_empty()),
2640        // JLS:字面量编译期即驻留池——"lit".intern() ≡ "lit"(同引用)
2641        ("intern", []) => Lit::Str(s.to_string()),
2642        ("trim", []) => Lit::Str(s.trim_matches(|c: char| c <= ' ').to_string()),
2643        ("concat", [Lit::Str(b)]) => Lit::Str(format!("{s}{b}")),
2644        ("startsWith", [Lit::Str(p)]) => Lit::Bool(s.starts_with(p.as_str())),
2645        ("endsWith", [Lit::Str(p)]) => Lit::Bool(s.ends_with(p.as_str())),
2646        ("contains", [Lit::Str(p)]) => Lit::Bool(s.contains(p.as_str())),
2647        ("equals", [Lit::Str(p)]) => Lit::Bool(s == p),
2648        ("equalsIgnoreCase", [Lit::Str(p)]) => {
2649            Lit::Bool(s.to_lowercase() == p.to_lowercase())
2650        }
2651        ("indexOf", [Lit::Char(c)]) => Lit::Int(
2652            s.find(*c).map(|i| i as i64).unwrap_or(-1),
2653        ),
2654        ("indexOf", [Lit::Str(p)]) => Lit::Int(
2655            s.find(p.as_str()).map(|i| i as i64).unwrap_or(-1),
2656        ),
2657        ("length", []) => Lit::Int(n),
2658        ("charAt", [Lit::Int(i)]) if *i >= 0 && *i < n => {
2659            Lit::Char(chars[*i as usize])
2660        }
2661        ("substring", [Lit::Int(b)]) if *b >= 0 && *b <= n => {
2662            Lit::Str(s.chars().skip(*b as usize).collect())
2663        }
2664        ("substring", [Lit::Int(b), Lit::Int(e)]) if *b >= 0 && *b <= *e && *e <= n => {
2665            Lit::Str(chars[*b as usize..*e as usize].iter().collect())
2666        }
2667        ("replace", [Lit::Char(a), Lit::Char(b)]) => {
2668            Lit::Str(s.chars().map(|c| if c == *a { *b } else { c }).collect())
2669        }
2670        ("replace", [Lit::Str(a), Lit::Str(b)]) if !a.is_empty() => {
2671            Lit::Str(s.replace(a.as_str(), b))
2672        }
2673        // String.hashCode 是 JLS 规定的确定性算法
2674        ("hashCode", []) => Lit::Int(
2675            s.chars()
2676                .fold(0i32, |h, c| h.wrapping_mul(31).wrapping_add(c as i32))
2677                as i64,
2678        ),
2679        _ => return None,
2680    })
2681}
2682
2683fn eval_static_method(cls: &str, method: &str, args: &[Lit]) -> Option<Lit> {
2684    let cls = cls.rsplit('.').next().unwrap_or(cls);
2685    Some(match (cls, method, args) {
2686        ("Integer", "parseInt", [Lit::Str(s)]) => {
2687            Lit::Int(s.trim().parse::<i64>().ok()?)
2688        }
2689        ("Integer", "toString", [Lit::Int(v)]) => Lit::Str(v.to_string()),
2690        ("Integer", "toString", [Lit::Long(v)]) => Lit::Str(v.to_string()),
2691        ("Long", "parseLong", [Lit::Str(s)]) => Lit::Long(s.trim().parse::<i64>().ok()?),
2692        ("Long", "toString", [Lit::Int(v)]) => Lit::Str(v.to_string()),
2693        ("Long", "toString", [Lit::Long(v)]) => Lit::Str(v.to_string()),
2694        ("Boolean", "parseBoolean", [Lit::Str(s)]) => {
2695            Lit::Bool(s.eq_ignore_ascii_case("true"))
2696        }
2697        ("Boolean", "toString", [Lit::Bool(b)]) => Lit::Str(b.to_string()),
2698        ("String", "valueOf", [Lit::Str(s)]) => Lit::Str(s.clone()),
2699        ("String", "valueOf", [Lit::Int(v)]) => Lit::Str(v.to_string()),
2700        ("String", "valueOf", [Lit::Long(v)]) => Lit::Str(v.to_string()),
2701        ("String", "valueOf", [Lit::Char(c)]) => Lit::Str(c.to_string()),
2702        ("String", "valueOf", [Lit::Bool(b)]) => Lit::Str(b.to_string()),
2703        ("String", "format", fmt_args @ [Lit::Str(_), ..]) => {
2704            let fmt = match &fmt_args[0] {
2705                Lit::Str(s) => s.clone(),
2706                _ => return None,
2707            };
2708            let out = eval_string_format(&fmt, &fmt_args[1..])?;
2709            Lit::Str(out)
2710        }
2711        ("Math", "abs", [Lit::Int(v)]) => Lit::Int(v.wrapping_abs()),
2712        ("Math", "abs", [Lit::Long(v)]) => Lit::Long(v.wrapping_abs()),
2713        ("Math", "max", [Lit::Int(a), Lit::Int(b)]) => Lit::Int(*a.max(b)),
2714        ("Math", "min", [Lit::Int(a), Lit::Int(b)]) => Lit::Int(*a.min(b)),
2715        ("Math", "max", [Lit::Long(a), Lit::Long(b)]) => Lit::Long(*a.max(b)),
2716        ("Math", "min", [Lit::Long(a), Lit::Long(b)]) => Lit::Long(*a.min(b)),
2717        ("Character", "isDigit", [Lit::Char(c)]) => Lit::Bool(c.is_ascii_digit()),
2718        ("Character", "isLetter", [Lit::Char(c)]) => Lit::Bool(c.is_alphabetic()),
2719        ("Character", "isWhitespace", [Lit::Char(c)]) => Lit::Bool(c.is_whitespace()),
2720        ("Character", "isUpperCase", [Lit::Char(c)]) => Lit::Bool(c.is_uppercase()),
2721        ("Character", "isLowerCase", [Lit::Char(c)]) => Lit::Bool(c.is_lowercase()),
2722        ("Character", "toUpperCase", [Lit::Char(c)]) => Lit::Char(c.to_uppercase().next()?),
2723        ("Character", "toLowerCase", [Lit::Char(c)]) => Lit::Char(c.to_lowercase().next()?),
2724        ("Character", "toString", [Lit::Char(c)]) => Lit::Str(c.to_string()),
2725        _ => return None,
2726    })
2727}
2728
2729/// String.format 最小子集:%s、%d、%%;其余转换(%f/%x/宽度/精度)不折。
2730fn eval_string_format(fmt: &str, args: &[Lit]) -> Option<String> {
2731    let mut out = String::new();
2732    let mut it = fmt.chars().peekable();
2733    let mut arg_i = 0usize;
2734    while let Some(c) = it.next() {
2735        if c != '%' {
2736            out.push(c);
2737            continue;
2738        }
2739        match it.next()? {
2740            '%' => out.push('%'),
2741            's' => {
2742                let a = args.get(arg_i)?;
2743                arg_i += 1;
2744                match a {
2745                    Lit::Str(s) => out.push_str(s),
2746                    Lit::Char(ch) => out.push(*ch),
2747                    Lit::Bool(b) => out.push_str(&b.to_string()),
2748                    _ => out.push_str(&lit_debug_str(a)),
2749                }
2750            }
2751            'd' => {
2752                let a = args.get(arg_i)?;
2753                arg_i += 1;
2754                match a {
2755                    Lit::Int(v) => out.push_str(&v.to_string()),
2756                    Lit::Long(v) => out.push_str(&v.to_string()),
2757                    Lit::Char(ch) => out.push_str(&(*ch as i64).to_string()),
2758                    _ => return None,
2759                }
2760            }
2761            // 未知/未支持转换:交给运行时
2762            _ => return None,
2763        }
2764    }
2765    if arg_i != args.len() {
2766        return None; // 实参数量与转换符不匹配 → 运行时异常路径,不折
2767    }
2768    Some(out)
2769}
2770
2771fn lit_debug_str(l: &Lit) -> String {
2772    match l {
2773        Lit::Int(v) => v.to_string(),
2774        Lit::Long(v) => v.to_string(),
2775        _ => "?".into(),
2776    }
2777}
2778
2779// ---------------------------------------------------------------------------
2780// Base64 字符串解码还原(混淆器标准藏匿手段):
2781//   new String(Base64.getDecoder().decode("aGVsbG8=")) → "hello"
2782//   (URL 解码器同理;解码字节须为合法 UTF-8)
2783// ---------------------------------------------------------------------------
2784
2785
2786// ---------------------------------------------------------------------------
2787// URL 解码还原(混淆器 URL 编码字符串的逆操作):
2788//   URLDecoder.decode("%E4%BD%A0", "UTF-8") → "你"
2789// 守卫:
2790//   - 两个参数全字面量;UTF-8/Locale.CHINA 等常规字符集名(无 charset
2791//     的单参形态按 UTF-8——JLS 规定 URLDecoder 无 charset 重载不存在,
2792//     单参是 6 字节 UTF-8 缺省过时形态,保守拒绝)
2793//   - 解码产物必须**全部可见**(控制字符/代理位不转——用户规则:
2794//     不可见字符不转换)
2795//   - 编码串 ≤ 8KB(超长解码性能保护——用户规则:特别长的跳过)
2796//   - 解码失败(非法 % 序列/未知 charset)→ 保持原样
2797// ---------------------------------------------------------------------------
2798
2799
2800/// Member 节点的成员名(Sym → str)。
2801fn member_name(ast: &JavaAst, m: JavaId) -> Option<String> {
2802    match ast.data(m) {
2803        NodeData::Member { name } => Some(ast.sn(*name).to_string()),
2804        _ => None,
2805    }
2806}
2807
2808pub struct UrlDecodeFold;
2809
2810impl Rule<JavaAst> for UrlDecodeFold {
2811    fn name(&self) -> &'static str {
2812        "url_decode_fold"
2813    }
2814    fn kinds(&self) -> &'static [NodeKind] {
2815        &[NodeKind::Call]
2816    }
2817    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
2818        let lang = ctx.lang;
2819        // 形态:URLDecoder.decode(s, charset)——callee 是 Member{decode},
2820        // 接收者是 VarRef/Member 链(校验末端名字)
2821        let ch = lang.children(id).to_vec();
2822        if ch.len() != 3 {
2823            return None; // 只处理双参形态
2824        }
2825        let callee = ch[0];
2826        if lang.kind(callee) != NodeKind::Member {
2827            return None;
2828        }
2829        if member_name(lang, callee).as_deref() != Some("decode") {
2830            return None;
2831        }
2832        // 接收者链:URLDecoder(Member{URLDecoder} 或 Member{net.URLDecoder})
2833        let recv = *lang.children(callee).first()?;
2834        let recv_name = match lang.kind(recv) {
2835            NodeKind::Member => member_name(lang, recv)?,
2836            NodeKind::VarRef => lang.var_name(recv)?.to_string(),
2837            _ => return None,
2838        };
2839        if !recv_name.ends_with("URLDecoder") {
2840            return None;
2841        }
2842        let s_lit = match lang.literal(ch[1])? {
2843            LitRef::Str(s) => s.to_string(),
2844            _ => return None,
2845        };
2846        // charset:仅接受 "UTF-8"(其余保守拒绝)
2847        let cs = match lang.literal(ch[2])? {
2848            LitRef::Str(s) => s.to_string(),
2849            _ => return None,
2850        };
2851        if cs != "UTF-8" {
2852            return None;
2853        }
2854        // 超长跳过
2855        if s_lit.len() > 8192 {
2856            return None;
2857        }
2858        let decoded = url_decode(&s_lit)?;
2859        // 可见性:控制字符/代理位 → 不转换
2860        if decoded.chars().any(|c| {
2861            c.is_control() || (c as u32) >= 0xD800 && (c as u32) <= 0xDFFF
2862        }) {
2863            return None;
2864        }
2865        let lit = lang.lit(Lit::Str(decoded));
2866        Some(Edit::Replace { target: id, with: lit })
2867    }
2868}
2869
2870/// 手写 URL 解码(%XX + '+' → 空格),UTF-8 字节重组。
2871/// 非法序列(%后非 hex、孤立 UTF-8 字节)→ None。
2872fn url_decode(s: &str) -> Option<String> {
2873    let b = s.as_bytes();
2874    let mut out: Vec<u8> = Vec::with_capacity(b.len());
2875    let mut i = 0usize;
2876    while i < b.len() {
2877        match b[i] {
2878            b'+' => {
2879                out.push(b' ');
2880                i += 1;
2881            }
2882            b'%' => {
2883                if i + 3 > b.len() {
2884                    return None;
2885                }
2886                let hi = hex_val(b[i + 1])?;
2887                let lo = hex_val(b[i + 2])?;
2888                out.push(hi * 16 + lo);
2889                i += 3;
2890            }
2891            c => {
2892                out.push(c);
2893                i += 1;
2894            }
2895        }
2896    }
2897    String::from_utf8(out).ok()
2898}
2899
2900fn hex_val(c: u8) -> Option<u8> {
2901    match c {
2902        b'0'..=b'9' => Some(c - b'0'),
2903        b'a'..=b'f' => Some(c - b'a' + 10),
2904        b'A'..=b'F' => Some(c - b'A' + 10),
2905        _ => None,
2906    }
2907}
2908
2909pub struct Base64NewStringFold;
2910
2911impl Rule<JavaAst> for Base64NewStringFold {
2912    fn name(&self) -> &'static str {
2913        "base64_new_string_fold"
2914    }
2915    fn kinds(&self) -> &'static [NodeKind] {
2916        &[NodeKind::New]
2917    }
2918    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
2919        let root = ctx.root();
2920        let lang = ctx.lang;
2921        let NodeData::New { ty, .. } = lang.data(id) else {
2922            return None;
2923        };
2924        if !matches!(ty, JType::Ref(n) if n == "String" || n == "java.lang.String") {
2925            return None;
2926        }
2927        // 折叠产出池化字面量,改变 String 引用身份 → 守卫
2928        if lang.has_string_identity_compare(root) {
2929            return None;
2930        }
2931        let args = lang.children(id).to_vec();
2932        if args.len() != 1 {
2933            return None;
2934        }
2935        // 参数:Base64.getXxxDecoder().decode("lit")
2936        if lang.kind(args[0]) != NodeKind::Call {
2937            return None;
2938        }
2939        let dc = lang.children(args[0]).to_vec();
2940        if dc.len() != 2 {
2941            return None;
2942        }
2943        let NodeData::Member { name: dn } = lang.data(dc[0]) else {
2944            return None;
2945        };
2946        if lang.sn(*dn) != "decode" {
2947            return None;
2948        }
2949        // recv = Base64.getXxxDecoder()(调用)
2950        let recv = lang.children(dc[0])[0];
2951        if lang.kind(recv) != NodeKind::Call {
2952            return None;
2953        }
2954        let gc = lang.children(recv).to_vec();
2955        let NodeData::Member { name: gn } = lang.data(*gc.first()?) else {
2956            return None;
2957        };
2958        let url_safe = match lang.sn(*gn) {
2959            "getDecoder" => false,
2960            "getUrlDecoder" => true,
2961            _ => return None,
2962        };
2963        // owner = java.util.Base64(Member 链)或裸 Base64(VarRef)
2964        let owner = lang.children(gc[0])[0];
2965        let owner_ok = match lang.data(owner) {
2966            NodeData::Member { name } => lang.sn(*name) == "Base64",
2967            _ => matches!(lang.var_name(owner), Some(n) if n == "Base64" || n.ends_with(".Base64")),
2968        };
2969        if !owner_ok {
2970            return None;
2971        }
2972        let LitRef::Str(b64) = lang.literal(dc[1])? else {
2973            return None;
2974        };
2975        let bytes = base64_decode(b64, url_safe)?;
2976        let text = String::from_utf8(bytes).ok()?;
2977        let with = lang.build_str(&text);
2978        Some(Edit::Replace {
2979            target: id,
2980            with,
2981        })
2982    }
2983}
2984
2985/// 手写 base64 解码(标准/URL 字母表,允许缺省填充)。
2986fn base64_decode(s: &str, url_safe: bool) -> Option<Vec<u8>> {
2987    let val = |c: char| -> Option<u32> {
2988        match c {
2989            'A'..='Z' => Some(c as u32 - 'A' as u32),
2990            'a'..='z' => Some(c as u32 - 'a' as u32 + 26),
2991            '0'..='9' => Some(c as u32 - '0' as u32 + 52),
2992            '+' if !url_safe => Some(62),
2993            '/' if !url_safe => Some(63),
2994            '-' if url_safe => Some(62),
2995            '_' if url_safe => Some(63),
2996            _ => None,
2997        }
2998    };
2999    let clean: String = s.chars().filter(|&c| c != '=' && !c.is_whitespace()).collect();
3000    if clean.len() % 4 == 1 {
3001        return None;
3002    }
3003    let mut out = Vec::new();
3004    let mut buf = 0u32;
3005    let mut bits = 0u32;
3006    for c in clean.chars() {
3007        buf = (buf << 6) | val(c)?;
3008        bits += 6;
3009        if bits >= 8 {
3010            bits -= 8;
3011            out.push((buf >> bits) as u8);
3012            buf &= (1 << bits) - 1;
3013        }
3014    }
3015    Some(out)
3016}
3017
3018
3019// ---------------------------------------------------------------------------
3020// 控制流扁平化还原(CFF recovery,obfuscator.io / Allatori 风格):
3021//   int s = 0;
3022//   while (true) {
3023//       switch (s) {
3024//           case 0: { A(); s = 1; break; }
3025//           case 1: { if (c) { s = 2; } else { s = 3; } break; }
3026//           case 2: { B(); s = 5; break; }
3027//           case 3: { C(); s = 5; break; }
3028//       }
3029//       if (s == 5) break;      // 或 default: return
3030//   }
3031//   →  A(); if (c) { B(); } else { C(); }
3032//
3033// 状态图 → 结构化控制流:
3034//   线性链顺序拼接;菱形找公共后继 if/else{前缀}+续接(单次发射);
3035//   分支回环到条件状态 → while(cond){体};链内后向边 → while(true){后缀}。
3036//   其余形态(发散/外部跳转/嵌套异常)保守拒绝。
3037// 守卫:v 仅作状态变量、循环后不使用、全部 case 可达、每状态体恰发射一次。
3038// ---------------------------------------------------------------------------
3039
3040pub struct CffRecover;
3041
3042struct CffCase {
3043    body: Vec<JavaId>,
3044    tail: CffTail,
3045}
3046
3047enum CffTail {
3048    Goto(i64),
3049    Cond { cond: JavaId, a: i64, b: i64 },
3050    Term(JavaId),
3051    Exit,
3052}
3053
3054#[derive(Clone, Copy, PartialEq, Debug)]
3055enum Flow {
3056    Done,
3057    Stop,
3058    Cycle(usize),
3059    Cond(i64),
3060    External(i64),
3061}
3062
3063struct CffCtx<'a> {
3064    lang: &'a mut JavaAst,
3065    cases: std::collections::HashMap<i64, CffCase>,
3066    /// default: return 的语句(goto 未知状态时发射)
3067    default_term: Option<Vec<JavaId>>,
3068    used_default: bool,
3069    /// 可达性记账(含试探性分支走查)
3070    reached: std::collections::HashSet<i64>,
3071    /// 已实际发射的状态(分支试探不得穿入)
3072    emitted: std::collections::HashSet<i64>,
3073}
3074
3075impl Rule<JavaAst> for CffRecover {
3076    fn name(&self) -> &'static str {
3077        "cff_recover"
3078    }
3079    fn kinds(&self) -> &'static [NodeKind] {
3080        &[NodeKind::While]
3081    }
3082    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
3083        let parent = ctx.parent(id)?;
3084        let idx = ctx.index(id)?;
3085        let lang = ctx.lang;
3086        if lang.kind(id) != NodeKind::While {
3087            return None;
3088        }
3089        let wch = lang.children(id).to_vec();
3090        if !matches!(lang.literal(wch[0]), Some(LitRef::Bool(true))) {
3091            return None;
3092        }
3093        if lang.kind(wch[1]) != NodeKind::Block {
3094            return None;
3095        }
3096                let body_ch = lang.children(wch[1]).to_vec();
3097        let (switch, exit_if) = match body_ch.len() {
3098            1 => (body_ch[0], None),
3099            2 => (body_ch[0], Some(body_ch[1])),
3100            _ => return None,
3101        };
3102        if lang.kind(switch) != NodeKind::Switch {
3103            return None;
3104        }
3105        let sch = lang.children(switch).to_vec();
3106        if lang.kind(sch[0]) != NodeKind::VarRef || !lang.is_local_var(sch[0]) {
3107            return None;
3108        }
3109        let v = lang.var_name(sch[0])?.to_string();
3110
3111        // 尾随出口:if (v == SENT) break;
3112        let sentinel = match exit_if {
3113            None => None,
3114            Some(ei) => {
3115                if lang.kind(ei) != NodeKind::If {
3116                    return None;
3117                }
3118                let ich = lang.children(ei).to_vec();
3119                if ich.len() != 2 || lang.bin_op(ich[0]) != Some(BinOp::Eq) {
3120                    return None;
3121                }
3122                let cch = lang.children(ich[0]).to_vec();
3123                let sent = if lang.kind(cch[0]) == NodeKind::VarRef
3124                    && lang.var_name(cch[0]) == Some(v.as_str())
3125                {
3126                    lang.literal(cch[1]).and_then(|x| x.as_int())
3127                } else if lang.kind(cch[1]) == NodeKind::VarRef
3128                    && lang.var_name(cch[1]) == Some(v.as_str())
3129                {
3130                    lang.literal(cch[0]).and_then(|x| x.as_int())
3131                } else {
3132                    return None;
3133                }?;
3134                let then_ok = match lang.kind(ich[1]) {
3135                    NodeKind::Break => matches!(lang.data(ich[1]), NodeData::Break { label: None }),
3136                    NodeKind::Block if lang.children(ich[1]).len() == 1 => {
3137                        matches!(lang.data(lang.children(ich[1])[0]), NodeData::Break { label: None })
3138                    }
3139                    _ => false,
3140                };
3141                if !then_ok {
3142                    return None;
3143                }
3144                Some(sent)
3145            }
3146        };
3147
3148        // 解析 case 表
3149        let mut cases: std::collections::HashMap<i64, CffCase> = Default::default();
3150        let mut default_term: Option<Vec<JavaId>> = None;
3151        for &c in &sch[1..] {
3152            let NodeData::Case { labels, is_default, arrow } = lang.data(c) else {
3153                return None;
3154            };
3155            let (labels, is_default, arrow) = (*labels, *is_default, *arrow);
3156            let ch = lang.children(c).to_vec();
3157            let mut stmts: Vec<JavaId> = ch[labels as usize..].to_vec();
3158            // 经典形态:剥尾部 break(块外或块内)
3159            if !arrow
3160                && stmts
3161                    .last()
3162                    .is_some_and(|&n| matches!(lang.data(n), NodeData::Break { label: None }))
3163            {
3164                stmts.pop();
3165            }
3166            if stmts.len() == 1 && lang.kind(stmts[0]) == NodeKind::Block {
3167                let mut inner = lang.children(stmts[0]).to_vec();
3168                if !arrow
3169                    && inner
3170                        .last()
3171                        .is_some_and(|&n| matches!(lang.data(n), NodeData::Break { label: None }))
3172                {
3173                    inner.pop();
3174                }
3175                stmts = inner;
3176            }
3177            if is_default {
3178                if stmts.len() == 1 && lang.kind(stmts[0]) == NodeKind::Return {
3179                    default_term = Some(stmts);
3180                    continue;
3181                }
3182                return None;
3183            }
3184            // 状态标签:单一整数字面量
3185            if labels != 1 {
3186                return None;
3187            }
3188            let state = match lang.literal(ch[0]) {
3189                Some(LitRef::Int(v)) => v,
3190                _ => return None,
3191            };
3192            let tail = parse_cff_tail(lang, &v, &mut stmts)?;
3193            // 尾部剥离后,剩余体不得引用状态变量
3194            for &st in &stmts {
3195                if subtree_has_var(lang, st, &v) {
3196                    return None;
3197                }
3198            }
3199            cases.insert(state, CffCase { body: stmts, tail });
3200        }
3201        if cases.is_empty() {
3202            return None;
3203        }
3204
3205        // 入口:向上扫描(最多 8 条)找设置 v = IntLit 的语句;
3206        // 中间语句不得有 v 事件(读写皆拒)
3207        if idx == 0 {
3208            return None;
3209        }
3210        let block_ch = lang.children(parent).to_vec();
3211        let mut init = None;
3212        let mut init_idx = idx;
3213        {
3214            let mut up = idx;
3215            for _ in 0..8 {
3216                if up == 0 {
3217                    break;
3218                }
3219                up -= 1;
3220                let st = block_ch[up];
3221                match scan_cff_event(lang, st, &v) {
3222                    Some(CffEvent::Read) => return None,
3223                    Some(CffEvent::Write) | Some(CffEvent::Decl) => {
3224                        init = parse_cff_init(lang, &v, st);
3225                        init_idx = up;
3226                        break;
3227                    }
3228                    None => continue,
3229                }
3230            }
3231        }
3232        let init = init?;
3233
3234        // 循环后 v 不得被使用
3235        for &st in &block_ch[idx + 1..] {
3236            if subtree_has_var(lang, st, &v) {
3237                return None;
3238            }
3239        }
3240
3241        // Goto(未知状态) → Exit(须有出口机制)
3242        let mut to_exit: Vec<i64> = Vec::new();
3243        for (k, ci) in cases.iter() {
3244            if let CffTail::Goto(g) = ci.tail {
3245                if !cases.contains_key(&g) {
3246                    if !(sentinel == Some(g) || default_term.is_some()) {
3247                        return None;
3248                    }
3249                    to_exit.push(*k);
3250                }
3251            }
3252        }
3253        for k in to_exit {
3254            if let Some(ci) = cases.get_mut(&k) {
3255                ci.tail = CffTail::Exit;
3256            }
3257        }
3258
3259        let mut c = CffCtx {
3260            lang,
3261            cases,
3262            default_term,
3263            used_default: false,
3264            reached: Default::default(),
3265            emitted: Default::default(),
3266        };
3267        let stmts = cff_run(&mut c, init)?;
3268
3269        // 不可达 case 是死代码(从入口不可达 → 永不执行),可安全丢弃;
3270        // default 未被走到同理。至少要求发射序列非空。
3271        if stmts.is_empty() {
3272            return None;
3273        }
3274
3275        // 用恢复序列替换 while + 删除 init 语句(两处独立位置:init 与 while
3276        // 之间的无关语句保留;顺序先 Splice(较大索引)后 Delete)
3277        Some(Edit::Multi(vec![
3278            Edit::Splice {
3279                node: parent,
3280                index: idx,
3281                remove: 1,
3282                insert: stmts,
3283            },
3284            Edit::Delete {
3285                node: block_ch[init_idx],
3286            },
3287        ]))
3288    }
3289}
3290
3291fn parse_cff_tail(lang: &JavaAst, v: &str, stmts: &mut Vec<JavaId>) -> Option<CffTail> {
3292    let last = *stmts.last()?;
3293    if lang.kind(last) == NodeKind::Return {
3294        stmts.pop();
3295        return Some(CffTail::Term(last));
3296    }
3297    let assign_of = |n: JavaId| -> Option<JavaId> {
3298        let inner = match lang.kind(n) {
3299            NodeKind::Assign => n,
3300            NodeKind::ExprStmt => {
3301                let ch = lang.children(n);
3302                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
3303                    ch[0]
3304                } else {
3305                    return None;
3306                }
3307            }
3308            _ => return None,
3309        };
3310        if lang.assign_op(inner).is_some() {
3311            return None;
3312        }
3313        let ch = lang.children(inner).to_vec();
3314        if lang.kind(ch[0]) != NodeKind::VarRef || lang.var_name(ch[0]) != Some(v) {
3315            return None;
3316        }
3317        Some(inner)
3318    };
3319    if let Some(assign) = assign_of(last) {
3320        let value = lang.children(assign)[1];
3321        if let Some(k) = lang.literal(value).and_then(|x| x.as_int()) {
3322            stmts.pop();
3323            return Some(CffTail::Goto(k));
3324        }
3325        if lang.kind(value) == NodeKind::Ternary {
3326            let tch = lang.children(value).to_vec();
3327            let a = lang.literal(tch[1]).and_then(|x| x.as_int())?;
3328            let b = lang.literal(tch[2]).and_then(|x| x.as_int())?;
3329            stmts.pop();
3330            return Some(CffTail::Cond { cond: tch[0], a, b });
3331        }
3332        return None;
3333    }
3334    if lang.kind(last) == NodeKind::If {
3335        let ich = lang.children(last).to_vec();
3336        if ich.len() != 3 {
3337            return None;
3338        }
3339        let arm = |n: JavaId| -> Option<i64> {
3340            let target = match lang.kind(n) {
3341                NodeKind::Assign | NodeKind::ExprStmt => assign_of(n)?,
3342                NodeKind::Block if lang.children(n).len() == 1 => assign_of(lang.children(n)[0])?,
3343                _ => return None,
3344            };
3345            lang.literal(lang.children(target)[1]).and_then(|x| x.as_int())
3346        };
3347        let a = arm(ich[1])?;
3348        let b = arm(ich[2])?;
3349        stmts.pop();
3350        return Some(CffTail::Cond { cond: ich[0], a, b });
3351    }
3352    None
3353}
3354
3355
3356/// CFF 入口向上扫描的事件判定(必经路径语义)。
3357enum CffEvent {
3358    Read,
3359    Write,
3360    /// v 自身的声明(含 init 时为入口源)
3361    Decl,
3362}
3363
3364fn scan_cff_event(lang: &JavaAst, node: JavaId, name: &str) -> Option<CffEvent> {
3365    match lang.kind(node) {
3366        NodeKind::VarRef => {
3367            if lang.var_name(node) == Some(name) {
3368                Some(CffEvent::Read)
3369            } else {
3370                None
3371            }
3372        }
3373        NodeKind::VarDecl => {
3374            if lang.var_name(node) == Some(name) {
3375                Some(CffEvent::Decl)
3376            } else {
3377                for &c in lang.children(node) {
3378                    if let Some(e) = scan_cff_event(lang, c, name) {
3379                        return Some(e);
3380                    }
3381                }
3382                None
3383            }
3384        }
3385        NodeKind::Assign => {
3386            let ch = lang.children(node);
3387            if let Some(&val) = ch.get(1) {
3388                if let Some(e) = scan_cff_event(lang, val, name) {
3389                    if matches!(e, CffEvent::Read) {
3390                        return Some(e);
3391                    }
3392                }
3393            }
3394            if lang.assign_op(node).is_none() {
3395                if let Some(&t) = ch.first() {
3396                    if lang.kind(t) == NodeKind::VarRef && lang.var_name(t) == Some(name) {
3397                        return Some(CffEvent::Write);
3398                    }
3399                }
3400            }
3401            None
3402        }
3403        NodeKind::If | NodeKind::While => scan_cff_event(lang, *lang.children(node).first()?, name),
3404        NodeKind::Ternary => scan_cff_event(lang, *lang.children(node).first()?, name),
3405        NodeKind::Binary => {
3406            let op = lang.bin_op(node)?;
3407            let ch = lang.children(node);
3408            if op.is_short_circuit() {
3409                scan_cff_event(lang, *ch.first()?, name)
3410            } else {
3411                for &c in ch {
3412                    if let Some(e) = scan_cff_event(lang, c, name) {
3413                        return Some(e);
3414                    }
3415                }
3416                None
3417            }
3418        }
3419        _ => {
3420            for &c in lang.children(node) {
3421                if let Some(e) = scan_cff_event(lang, c, name) {
3422                    return Some(e);
3423                }
3424            }
3425            None
3426        }
3427    }
3428}
3429
3430fn parse_cff_init(lang: &JavaAst, v: &str, stmt: JavaId) -> Option<i64> {
3431    match lang.kind(stmt) {
3432        NodeKind::VarDecl => {
3433            let ch = lang.children(stmt).to_vec();
3434            if ch.len() != 1 || lang.var_name(stmt) != Some(v) {
3435                return None;
3436            }
3437            lang.literal(ch[0]).and_then(|x| x.as_int())
3438        }
3439        NodeKind::Assign => {
3440            if lang.assign_op(stmt).is_some() {
3441                return None;
3442            }
3443            let ch = lang.children(stmt).to_vec();
3444            if lang.kind(ch[0]) != NodeKind::VarRef || lang.var_name(ch[0]) != Some(v) {
3445                return None;
3446            }
3447            lang.literal(ch[1]).and_then(|x| x.as_int())
3448        }
3449        NodeKind::ExprStmt => {
3450            let ch = lang.children(stmt).to_vec();
3451            if ch.len() == 1 {
3452                parse_cff_init(lang, v, ch[0])
3453            } else {
3454                None
3455            }
3456        }
3457        _ => None,
3458    }
3459}
3460
3461fn flatten(pairs: &[(i64, Vec<JavaId>)], out: &mut Vec<JavaId>, c: &mut CffCtx) {
3462    for (s, stmts) in pairs {
3463        c.emitted.insert(*s);
3464        out.extend(stmts.iter().copied());
3465    }
3466}
3467
3468fn flatten_prefix(pairs: &[(i64, Vec<JavaId>)], m: i64, out: &mut Vec<JavaId>) {
3469    for (s, stmts) in pairs {
3470        if *s == m {
3471            break;
3472        }
3473        out.extend(stmts.iter().copied());
3474    }
3475}
3476
3477fn suffix_pairs(pairs: &[(i64, Vec<JavaId>)], m: i64) -> Vec<(i64, Vec<JavaId>)> {
3478    let pos = pairs.iter().position(|(s, _)| *s == m).unwrap();
3479    pairs[pos..].to_vec()
3480}
3481
3482/// 纯收集:沿无条件 goto 前进,不消费条件 case。
3483fn collect(c: &mut CffCtx, start: i64, stop: Option<i64>) -> Option<(Vec<(i64, Vec<JavaId>)>, Flow)> {
3484    let mut path: Vec<(i64, Vec<JavaId>)> = Vec::new();
3485    let mut cur = start;
3486    let mut guard = 0usize;
3487    loop {
3488        guard += 1;
3489        if guard > 10_000 {
3490            return None;
3491        }
3492        if Some(cur) == stop {
3493            return Some((path, Flow::Stop));
3494        }
3495        if let Some(i) = path.iter().position(|(s, _)| *s == cur) {
3496            return Some((path, Flow::Cycle(i)));
3497        }
3498        if c.emitted.contains(&cur) {
3499            return Some((path, Flow::External(cur)));
3500        }
3501        c.reached.insert(cur);
3502        let ci = c.cases.get(&cur)?;
3503        let mut body = ci.body.clone();
3504        match ci.tail {
3505            CffTail::Goto(k) => {
3506                path.push((cur, body));
3507                cur = k;
3508            }
3509            CffTail::Term(r) => {
3510                body.push(r);
3511                path.push((cur, body));
3512                return Some((path, Flow::Done));
3513            }
3514            CffTail::Exit => {
3515                if c.default_term.is_some() {
3516                    if let Some(dt) = c.default_term.clone() {
3517                        body.extend(dt);
3518                        c.used_default = true;
3519                    }
3520                }
3521                path.push((cur, body));
3522                return Some((path, Flow::Done));
3523            }
3524            CffTail::Cond { .. } => return Some((path, Flow::Cond(cur))),
3525        }
3526    }
3527}
3528
3529/// 顶层:从入口状态结构化整个状态机。
3530fn cff_run(c: &mut CffCtx, init: i64) -> Option<Vec<JavaId>> {
3531    let (path, flow) = collect(c, init, None)?;
3532    cff_structure(c, path, flow)
3533}
3534
3535/// 从某状态起递归结构化(混合终点分支的续接构造用)。
3536fn cff_run_from(c: &mut CffCtx, start: i64) -> Option<Vec<JavaId>> {
3537    let (path, flow) = collect(c, start, None)?;
3538    cff_structure(c, path, flow)
3539}
3540
3541/// 核心结构化循环:携带当前链(path + flow),直到终止。
3542/// 混合终点(一支终止、另一支续接)时,续接必须放进继续分支**内部**
3543/// (续接代码只在走该分支时执行)。
3544fn cff_structure(c: &mut CffCtx, mut path: Vec<(i64, Vec<JavaId>)>, mut flow: Flow) -> Option<Vec<JavaId>> {
3545    let mut stmts: Vec<JavaId> = Vec::new();
3546    loop {
3547        match flow {
3548            Flow::Done => {
3549                flatten(&path, &mut stmts, c);
3550                return Some(stmts);
3551            }
3552            Flow::Cycle(i) => {
3553                flatten(&path[..i], &mut stmts, c);
3554                let mut suffix = Vec::new();
3555                flatten(&path[i..], &mut suffix, c);
3556                let inner = c.lang.build_block(suffix);
3557                let cond = c.lang.build_bool(true);
3558                let w = c.lang.while_(cond, inner);
3559                stmts.push(w);
3560                return Some(stmts);
3561            }
3562            Flow::Stop | Flow::External(_) => return None,
3563            Flow::Cond(state) => {
3564                flatten(&path, &mut stmts, c);
3565                path.clear();
3566                let ci = c.cases.get(&state)?;
3567                stmts.extend(ci.body.iter().copied());
3568                c.emitted.insert(state);
3569                let CffTail::Cond { cond, a, b } = ci.tail else {
3570                    return None;
3571                };
3572                // 循环形态:分支走回条件状态
3573                let (pa, fa) = collect(c, a, Some(state))?;
3574                if fa == Flow::Stop {
3575                    let mut lb = Vec::new();
3576                    flatten(&pa, &mut lb, c);
3577                    let inner = c.lang.build_block(lb);
3578                    let w = c.lang.while_(cond, inner);
3579                    stmts.push(w);
3580                    let (p2, f2) = collect(c, b, None)?;
3581                    path = p2;
3582                    flow = f2;
3583                    continue;
3584                }
3585                let (pb, fb) = collect(c, b, Some(state))?;
3586                if fb == Flow::Stop {
3587                    let mut lb = Vec::new();
3588                    flatten(&pb, &mut lb, c);
3589                    let inner = c.lang.build_block(lb);
3590                    let nc = c.lang.build_unary(UnOp::Not, cond);
3591                    let w = c.lang.while_(nc, inner);
3592                    stmts.push(w);
3593                    let (p2, f2) = collect(c, a, None)?;
3594                    path = p2;
3595                    flow = f2;
3596                    continue;
3597                }
3598                // 菱形:两支共享后继
3599                let common = pa
3600                    .iter()
3601                    .find(|(s, _)| pb.iter().any(|(t, _)| *s == *t))
3602                    .map(|(s, _)| *s);
3603                let same_end = match (fa, fb) {
3604                    (Flow::Done, Flow::Done) => true,
3605                    (Flow::Cond(x), Flow::Cond(y)) => x == y,
3606                    _ => false,
3607                };
3608                match common {
3609                    Some(m) if same_end => {
3610                        let mut pre_a = Vec::new();
3611                        let mut pre_b = Vec::new();
3612                        flatten_prefix(&pa, m, &mut pre_a);
3613                        flatten_prefix(&pb, m, &mut pre_b);
3614                        let ba = c.lang.build_block(pre_a);
3615                        let bb = c.lang.build_block(pre_b);
3616                        let ifs = c.lang.if_(cond, ba, Some(bb));
3617                        stmts.push(ifs);
3618                        path = suffix_pairs(&pa, m);
3619                        flow = fa;
3620                        continue;
3621                    }
3622                    Some(_) => return None,
3623                    None => {
3624                        // 无公共后继的分叉
3625                        let mut fa_stmts = Vec::new();
3626                        let mut fb_stmts = Vec::new();
3627                        flatten(&pa, &mut fa_stmts, c);
3628                        flatten(&pb, &mut fb_stmts, c);
3629                        match (fa, fb) {
3630                            (Flow::Done, Flow::Done) => {
3631                                let ba = c.lang.build_block(fa_stmts);
3632                                let bb = c.lang.build_block(fb_stmts);
3633                                let ifs = c.lang.if_(cond, ba, Some(bb));
3634                                stmts.push(ifs);
3635                                return Some(stmts);
3636                            }
3637                            // 两支都续接到同一条件 → if/else 后无条件续接(合流)
3638                            (Flow::Cond(x), Flow::Cond(y)) if x == y => {
3639                                let ba = c.lang.build_block(fa_stmts);
3640                                let bb = c.lang.build_block(fb_stmts);
3641                                let ifs = c.lang.if_(cond, ba, Some(bb));
3642                                stmts.push(ifs);
3643                                let (p2, f2) = collect(c, x, None)?;
3644                                path = p2;
3645                                flow = f2;
3646                                continue;
3647                            }
3648                            // 混合:一支终止、另一支续接 → 续接放进继续分支【内部】
3649                            (Flow::Done, Flow::Cond(x)) => {
3650                                let cont = cff_run_from(c, x)?;
3651                                fb_stmts.extend(cont);
3652                                let ba = c.lang.build_block(fa_stmts);
3653                                let bb = c.lang.build_block(fb_stmts);
3654                                let ifs = c.lang.if_(cond, ba, Some(bb));
3655                                stmts.push(ifs);
3656                                return Some(stmts);
3657                            }
3658                            (Flow::Cond(x), Flow::Done) => {
3659                                let cont = cff_run_from(c, x)?;
3660                                fa_stmts.extend(cont);
3661                                let ba = c.lang.build_block(fa_stmts);
3662                                let bb = c.lang.build_block(fb_stmts);
3663                                let ifs = c.lang.if_(cond, ba, Some(bb));
3664                                stmts.push(ifs);
3665                                return Some(stmts);
3666                            }
3667                            _ => return None,
3668                        }
3669                    }
3670                }
3671            }
3672        }
3673    }
3674}
3675
3676
3677
3678// ---------------------------------------------------------------------------
3679// 跨方法常量知识(string-array 解密器的过程间分析入口):
3680// simplify_unit 把类级常量字段与单 return 方法体填入 JavaAst 侧表,
3681// 下面两条规则在 fixed point 中消费,与部分求值/数组下标/Base64 折叠
3682// 自然组合:d(0) → 方法体 → T[0] → "c3Vw" → Base64 解码 → "sup"。
3683// ---------------------------------------------------------------------------
3684
3685/// 收集类级常量:static final 字面量/数组字段(无写、无局部同名)
3686/// 与单 return 方法体。
3687pub fn collect_unit_consts(ast: &mut JavaAst, unit: &CompilationUnit) {
3688    ast.const_fields.clear();
3689    ast.const_scalars.clear();
3690    ast.inline_methods.clear();
3691    ast.field_types.clear();
3692    ast.final_fields.clear();
3693    // 已知泛型简单名(raw 接收位传播守卫用):单元内带 ty_params 的
3694    // 类型 + 常见 JDK/库泛型
3695    ast.raw_generic_names = {
3696        let mut m: std::collections::HashSet<String> = [
3697            "List", "ArrayList", "LinkedList", "Collection", "Set", "HashSet",
3698            "TreeSet", "LinkedHashSet", "Map", "HashMap", "TreeMap", "LinkedHashMap",
3699            "Hashtable", "SortedMap", "SortedSet", "NavigableMap", "NavigableSet",
3700            "Iterable", "Iterator", "ListIterator", "Comparator", "Optional",
3701            "Stream", "Class", "Comparable", "Entry", "AbstractList", "AbstractMap",
3702            "WeakHashMap", "IdentityHashMap", "ConcurrentHashMap", "ConcurrentMap",
3703            "Queue", "Deque", "ArrayDeque", "Vector", "Stack", "Enumeration",
3704            "Callable", "Future", "Supplier", "Function", "BiFunction", "Consumer",
3705            "Predicate", "UnaryOperator", "BinaryOperator", "UnmodifiableList",
3706            // 第 13 轮 P0-3 补充(R12 攻击代理建议 + T3k 实锤):
3707            "AbstractSet", "AbstractCollection", "AbstractQueue", "Spliterator",
3708            "EnumMap", "EnumSet", "PriorityQueue", "DelayQueue", "LinkedBlockingQueue",
3709            "LinkedTransferQueue", "ConcurrentLinkedQueue", "ConcurrentSkipListMap",
3710            "ConcurrentSkipListSet", "CopyOnWriteArrayList", "CopyOnWriteArraySet",
3711            "ArrayBlockingQueue", "SynchronousQueue", "ThreadLocal", "Reference",
3712            "SoftReference", "WeakReference", "AtomicReference", "AtomicStampedReference",
3713        ]
3714        .iter()
3715        .map(|x| x.to_string())
3716        .collect();
3717        fn collect_generics(ty: &TypeDecl, m: &mut std::collections::HashSet<String>) {
3718            if !ty.ty_params.trim().is_empty() {
3719                m.insert(ty.name.clone());
3720            }
3721            for mem in &ty.members {
3722                if let Member::Type(t) = mem {
3723                    collect_generics(t, m);
3724                }
3725            }
3726        }
3727        for ty in &unit.types {
3728            collect_generics(ty, &mut m);
3729        }
3730        m
3731    };
3732    ast.unit_shadows_lang = {
3733        fn decl_shadows(ty: &TypeDecl) -> bool {
3734            if ty.name == "String" || ty.name == "StringBuilder" {
3735                return true;
3736            }
3737            ty.members.iter().any(|m| match m {
3738                Member::Type(t) => decl_shadows(t),
3739                _ => false,
3740            })
3741        }
3742        unit.types.iter().any(decl_shadows)
3743    };
3744
3745    // 第一遍(递归含嵌套类型——规则也处理嵌套类体内的 VarRef):
3746    // 遮蔽集(局部/参数同名声明)+ 全单元名字写计数 + 字段声明名计数。
3747    // 写计数取代旧「写即除名」污染集:clinit 单写字面量路径需要知道
3748    // 「写过几次」而不只是「写过没有」。
3749    let mut shadowed: std::collections::HashSet<String> = Default::default();
3750    let mut write_counts: std::collections::HashMap<String, u32> = Default::default();
3751    let mut field_decls: std::collections::HashMap<String, u32> = Default::default();
3752    let mut bodies: Vec<JavaId> = Vec::new();
3753    for ty in &unit.types {
3754        collect_bodies_recursive(ty, &mut bodies, &mut shadowed, &mut field_decls);
3755    }
3756    // 字段类型表 + final 集合(调用点实参类型解析 / 截断守卫 finality 用;
3757    // 同名二见移除——保守)
3758    for ty in &unit.types {
3759        collect_field_types(ty, &mut ast.field_types, &mut ast.final_fields);
3760    }
3761    // 写扫描:赋值目标子树全部名字(含数组元素写——改内容同样破坏
3762    // 常量性)、++/-- 目标、Member 目标的成员名(this.a=x / A.a=x——
3763    // this.a 可写静态字段,裸名计数看不见)。
3764    for &root in &bodies {
3765        let mut stack = vec![root];
3766        while let Some(n) = stack.pop() {
3767            if ast.kind(n) == NodeKind::VarDecl {
3768                if let Some(nm) = ast.var_name(n) {
3769                    shadowed.insert(nm.to_string());
3770                }
3771            }
3772            match ast.data(n) {
3773                NodeData::Assign { .. } => {
3774                    let t = ast.children(n)[0];
3775                    if ast.kind(t) == NodeKind::Member {
3776                        if let NodeData::Member { name: sym } = ast.data(t) {
3777                            *write_counts.entry(ast.sn(*sym).to_string()).or_insert(0) += 1;
3778                        }
3779                    }
3780                    let mut ts = vec![t];
3781                    while let Some(x) = ts.pop() {
3782                        if ast.kind(x) == NodeKind::VarRef {
3783                            if let Some(nm) = ast.var_name(x) {
3784                                *write_counts.entry(nm.to_string()).or_insert(0) += 1;
3785                            }
3786                        }
3787                        for &c in ast.children(x) {
3788                            ts.push(c);
3789                        }
3790                    }
3791                }
3792                NodeData::Unary { op } if op.is_incdec() => {
3793                    let t = ast.children(n)[0];
3794                    if ast.kind(t) == NodeKind::VarRef {
3795                        if let Some(nm) = ast.var_name(t) {
3796                            *write_counts.entry(nm.to_string()).or_insert(0) += 1;
3797                        }
3798                    }
3799                }
3800                _ => {}
3801            }
3802            for &c in ast.children(n) {
3803                stack.push(c);
3804            }
3805        }
3806    }
3807
3808    // 第二遍:常量字段收集(声明初始化路径 + clinit 单写路径)
3809    for ty in &unit.types {
3810        // 本类型自己的 clinit 顶层字面量赋值(静态初始化块材料化形态)
3811        let mut own_lits: std::collections::HashMap<String, JavaId> = Default::default();
3812        collect_own_clinit_lits(ast, ty, &mut own_lits);
3813        for m in &ty.members {
3814            if let Member::Field { mods, ty: fty, declarators } = m {
3815                let words: Vec<&str> = mods.split_whitespace().collect();
3816                let is_static = words.contains(&"static");
3817                let is_final = words.contains(&"final");
3818                let is_private = words.contains(&"private");
3819                for d in declarators {
3820                    // 名字唯一性:全单元(含嵌套)恰一个同名字段声明。
3821                    // 嵌套类同名字段遮蔽外层(或外层遮蔽嵌套)——名字
3822                    // 全局的传播无法区分裸名解析指向哪个 → 双见即放弃。
3823                    if field_decls.get(&d.name).copied().unwrap_or(0) != 1 {
3824                        continue;
3825                    }
3826                    let init = match d.init {
3827                        Some(init) => {
3828                            // 声明初始化路径:final + 字面量 + 无写 + 无遮蔽
3829                            if !is_final
3830                                || !is_const_init(ast, init)
3831                                || shadowed.contains(&d.name)
3832                                || write_counts.get(&d.name).copied().unwrap_or(0) != 0
3833                            {
3834                                continue;
3835                            }
3836                            init
3837                        }
3838                        None => {
3839                            // clinit 单写路径:static final,或 static private
3840                            //(非 final 必须 private——限定名跨类写 A.a=v 的
3841                            // 目标是 Member 节点,名字计数对私有字段才无漏洞;
3842                            // bd.java:static_exec 坍缩 106 段解密循环成
3843                            // i = new String[]{…} / a = "SHA-512" 后经此路径
3844                            // 成为常量)
3845                            if !is_static
3846                                || (!is_final && !is_private)
3847                                || shadowed.contains(&d.name)
3848                                || write_counts.get(&d.name).copied().unwrap_or(0) != 1
3849                            {
3850                                continue;
3851                            }
3852                            match own_lits.get(&d.name) {
3853                                Some(&init) if is_const_init(ast, init) => init,
3854                                _ => continue,
3855                            }
3856                        }
3857                    };
3858                    // 同名二见 → 除名(跨类型保守去重,旧语义保持)
3859                    if ast.const_fields.contains_key(&d.name) {
3860                        ast.const_fields.remove(&d.name);
3861                        ast.const_scalars.remove(&d.name);
3862                    } else {
3863                        let scalar_ok = scalar_lit_of(ast, init)
3864                            .is_some_and(|l| lit_type_matches_decl(fty, l));
3865                        ast.const_fields.insert(d.name.clone(), init);
3866                        if scalar_ok {
3867                            ast.const_scalars.insert(d.name.clone());
3868                        }
3869                    }
3870                }
3871            }
3872        }
3873    }
3874
3875    // 单元级 String 身份比较标志(new String(lit) 解包守卫——字段 init
3876    // 的观察点在其他方法,单根扫描看不见)
3877    // 同一性断言方法名(assertNotSame/assertSame/isSameInstanceAs/
3878    // isNotSameInstanceAs——guava/mockito 测试框架的实例同一性断言:
3879    // new String("a") 折叠会翻转断言结果——ConcurrentHashMultisetTest
3880    // testIdentityKeyEquality 抓获)→ new String 折叠拒绝
3881    {
3882        let identity_assert = [
3883            "assertSame",
3884            "assertNotSame",
3885            "isSameInstanceAs",
3886            "isNotSameInstanceAs",
3887            "referenceEquals",
3888            "same",
3889        ];
3890        let mut hit = false;
3891        for root in &bodies {
3892            let mut stack = vec![*root];
3893            while let Some(n) = stack.pop() {
3894                if let NodeData::Call = ast.data(n) {
3895                    if let Some(&callee) = ast.children(n).first() {
3896                        let nm = match ast.data(callee) {
3897                            NodeData::Member { name } => ast.sn(*name),
3898                            NodeData::VarRef { name } => ast.sn(*name),
3899                            _ => "",
3900                        };
3901                        if identity_assert.contains(&nm) {
3902                            hit = true;
3903                            break;
3904                        }
3905                    }
3906                }
3907                for &c in ast.children(n) {
3908                    stack.push(c);
3909                }
3910            }
3911            if hit {
3912                break;
3913            }
3914        }
3915        ast.unit_string_identity =
3916            ast.any_string_identity(&bodies) || hit;
3917    }
3918
3919    // 单 return 方法收集(仅**不可覆写**方法:private/static/final——
3920    // UIAction.accept 抓获:protected 单 return 方法被子类覆写,
3921    // this.m(实参) 被内联成常量 → 虚分派破坏,XEmbeddedFrame 的
3922    // traverseOut @Override 整段副作用丢失)
3923    // **同名方法全单元计数**:重载存在(含被下方守卫拒绝的 varargs/匿名
3924    // 体形态——它们不落 insert 分支故旧 contains_key/remove 对撞失效)时
3925    // 名字整体否决——调用点无法静态分辨目标重载(javapoet TypeSpec 抓获:
3926    // anonymousClassBuilder("") 曾被绑定进 anonymousClassBuilder(CodeBlock)
3927    // 单 return 体 → javac 不兼容类型)
3928    let mut method_name_count: std::collections::HashMap<String, usize> =
3929        std::collections::HashMap::new();
3930    for_each_type(unit, &mut |ty: &TypeDecl| {
3931        for m in &ty.members {
3932            if let Member::Method { name, .. } = m {
3933                *method_name_count.entry(name.clone()).or_insert(0) += 1;
3934            }
3935        }
3936    });
3937    for ty in &unit.types {
3938        for m in &ty.members {
3939            if let Member::Method { mods, name, params, body: Some(b), .. } = m {
3940                let words: Vec<&str> = mods.split_whitespace().collect();
3941                let non_overridable = words.contains(&"private")
3942                    || words.contains(&"static")
3943                    || words.contains(&"final");
3944                if !non_overridable {
3945                    continue;
3946                }
3947                let ch = ast.children(*b).to_vec();
3948                if ch.len() == 1 && ast.kind(ch[0]) == NodeKind::Return {
3949                    if let Some(&expr) = ast.children(ch[0]).first() {
3950                        if params.len() <= 3
3951                            // 匿名类体:参数替换盲区(体内 return rms 的
3952                            // rms 是参数——内联后自由引用悬空——
3953                            // EvaluationRmsCheckerTest 抓获:16 文件输出
3954                            // 不可编译)→ 拒绝
3955                            && !subtree_has_anon(ast, expr)
3956                            // varargs 形参:单实参直通 `array(arg)` 的
3957                            // 内联丢 `new T[]{arg}` 包装——junit5 131 处
3958                            // 断言恒败 → 拒绝
3959                            && !params.iter().any(|p| p.varargs)
3960                            && !subtree_calls_self(ast, expr, name)
3961                            // 类型敏感守卫:字面量替换会改变形参位置的静态类型
3962                            // (switch 模式选择器 / instanceof 被测式 / Raw 不
3963                            // 可见构造)——`match(42)` 内联成 switch(42) 遭
3964                            // javac 拒绝(Adv6 差分抓获)。混淆 helper(纯算
3965                            // 术/字符串体)不受影响。
3966                            && !subtree_has_type_sensitive(ast, expr)
3967                        && method_name_count.get(name).copied() == Some(1)
3968                        {
3969                            let ps: Vec<String> = params.iter().map(|p| p.name.clone()).collect();
3970                            ast.inline_methods.insert(name.clone(), (ps, expr));
3971                        }
3972                    }
3973                }
3974            }
3975        }
3976    }
3977}
3978
3979/// 常量初始化判定:字面量,或字面量数组(new T[]{…} / {…})。
3980fn is_const_init(ast: &JavaAst, init: JavaId) -> bool {
3981    match ast.kind(init) {
3982        NodeKind::Literal => true,
3983        NodeKind::ArrayLit => ast.children(init).iter().all(|&e| ast.kind(e) == NodeKind::Literal),
3984        NodeKind::NewArray => {
3985            let ch = ast.children(init).to_vec();
3986            ch.len() == 1
3987                && ast.kind(ch[0]) == NodeKind::ArrayLit
3988                && ast.children(ch[0]).iter().all(|&e| ast.kind(e) == NodeKind::Literal)
3989        }
3990        _ => false,
3991    }
3992}
3993
3994/// 递归收集类型体内(含嵌套类型):方法/构造器体、初始化块、字段初始化
3995/// 表达式 → bodies;参数与局部声明名 → shadowed;字段声明名计数 →
3996/// field_decls。
3997fn collect_bodies_recursive(
3998    ty: &TypeDecl,
3999    bodies: &mut Vec<JavaId>,
4000    shadowed: &mut std::collections::HashSet<String>,
4001    field_decls: &mut std::collections::HashMap<String, u32>,
4002) {
4003    for m in &ty.members {
4004        match m {
4005            Member::Method { body: Some(b), params, .. }
4006            | Member::Constructor { body: Some(b), params, .. } => {
4007                bodies.push(*b);
4008                for p in params {
4009                    shadowed.insert(p.name.clone());
4010                }
4011            }
4012            Member::Initializer { body, .. } => bodies.push(*body),
4013            Member::Field { declarators, .. } => {
4014                for d in declarators {
4015                    *field_decls.entry(d.name.clone()).or_insert(0) += 1;
4016                    if let Some(init) = d.init {
4017                        bodies.push(init);
4018                    }
4019                }
4020            }
4021            Member::Type(t) => {
4022                collect_bodies_recursive(t, bodies, shadowed, field_decls);
4023            }
4024            _ => {}
4025        }
4026    }
4027}
4028
4029/// 递归收集字段声明类型(名字 → JType)与 final 集合(同名二见移除)。
4030fn collect_field_types(
4031    ty: &TypeDecl,
4032    out: &mut std::collections::HashMap<String, JType>,
4033    finals: &mut std::collections::HashSet<String>,
4034) {
4035    for m in &ty.members {
4036        match m {
4037            Member::Field { mods, ty: fty, declarators } => {
4038                let is_final = mods.split_whitespace().any(|w| w == "final");
4039                for d in declarators {
4040                    if out.contains_key(&d.name) {
4041                        out.remove(&d.name);
4042                        finals.remove(&d.name);
4043                    } else {
4044                        out.insert(d.name.clone(), fty.clone());
4045                        if is_final {
4046                            finals.insert(d.name.clone());
4047                        }
4048                    }
4049                }
4050            }
4051            Member::Type(t) => collect_field_types(t, out, finals),
4052            _ => {}
4053        }
4054    }
4055}
4056
4057/// 本类型静态初始化块顶层 `name = <常量>` 赋值收集(static_exec 材料
4058/// 化后的形态)。只看本类型自己的 clinit:嵌套类初始化块里的裸名赋值
4059/// 指向嵌套自己的静态字段(或外层字段——由写计数+唯一性守卫兜底),
4060/// 不能归因到外层声明。
4061fn collect_own_clinit_lits(
4062    ast: &JavaAst,
4063    ty: &TypeDecl,
4064    out: &mut std::collections::HashMap<String, JavaId>,
4065) {
4066    for m in &ty.members {
4067        if let Member::Initializer { is_static: true, body } = m {
4068            if ast.kind(*body) != NodeKind::Block {
4069                continue;
4070            }
4071            for &st in ast.children(*body) {
4072                let asg = match ast.kind(st) {
4073                    NodeKind::ExprStmt => {
4074                        let c = ast.children(st)[0];
4075                        if ast.kind(c) == NodeKind::Assign {
4076                            c
4077                        } else {
4078                            continue;
4079                        }
4080                    }
4081                    NodeKind::Assign => st,
4082                    _ => continue,
4083                };
4084                let ch = ast.children(asg).to_vec();
4085                if ast.kind(ch[0]) != NodeKind::VarRef {
4086                    continue;
4087                }
4088                let Some(nm) = ast.var_name(ch[0]) else { continue };
4089                if is_const_init(ast, ch[1]) {
4090                    out.insert(nm.to_string(), ch[1]);
4091                }
4092            }
4093        }
4094    }
4095}
4096
4097/// 标量字面量提取(数组/复合形式 → None:只走下标折叠,不整表复制)。
4098fn scalar_lit_of(ast: &JavaAst, init: JavaId) -> Option<cure_engine::kind::LitRef<'_>> {
4099    if ast.kind(init) == NodeKind::Literal {
4100        ast.literal(init)
4101    } else {
4102        None
4103    }
4104}
4105
4106/// 字面量种类与字段声明类型**精确匹配**:VarRef 的静态类型是声明类型,
4107/// 字面量的类型是字面量种类——过载解析对此敏感(`final long L = 5` 的
4108/// 5 是 int 字面量:m(L) 绑定 m(long),m(5) 绑定 m(int))。不匹配 →
4109/// 拒绝传播(收集期一次性守卫,bd.java 的 String a/j/f 均精确匹配)。
4110fn lit_type_matches_decl(fty: &JType, lit: cure_engine::kind::LitRef) -> bool {
4111    use cure_engine::kind::LitRef;
4112    match (fty, lit) {
4113        (JType::Ref(r), LitRef::Str(_)) => {
4114            let base = r.split('<').next().unwrap_or(r).trim();
4115            base == "String" || base == "java.lang.String"
4116        }
4117        (JType::Char, LitRef::Char(_)) => true,
4118        (JType::Bool, LitRef::Bool(_)) => true,
4119        (JType::Int, LitRef::Int(_)) => true,
4120        (JType::Long, LitRef::Long(_)) => true,
4121        (JType::Float, LitRef::Float(_)) => true,
4122        (JType::Double, LitRef::Double(_)) => true,
4123        _ => false,
4124    }
4125}
4126
4127/// 子树含类型敏感构造(switch / instanceof / Raw):字面量替换形参
4128/// 会改变这些位置的静态类型 → 编译器拒绝(`match(42)` → `switch(42)`
4129/// 遭 javac 否决,Adv6 差分抓获)。这类方法不进内联候选。
4130fn subtree_has_type_sensitive(ast: &JavaAst, root: JavaId) -> bool {
4131    let mut stack = vec![root];
4132    while let Some(n) = stack.pop() {
4133        match ast.kind(n) {
4134            NodeKind::Switch | NodeKind::InstanceOf | NodeKind::Raw => return true,
4135            _ => {}
4136        }
4137        for &c in ast.children(n) {
4138            stack.push(c);
4139        }
4140    }
4141    false
4142}
4143
4144fn subtree_has_anon(ast: &JavaAst, root: JavaId) -> bool {
4145    let mut stack = vec![root];
4146    while let Some(n) = stack.pop() {
4147        if let NodeData::New { anon_raw: Some(_), .. } = ast.data(n) {
4148            return true;
4149        }
4150        for &c in ast.children(n) {
4151            stack.push(c);
4152        }
4153    }
4154    false
4155}
4156
4157fn subtree_calls_self(ast: &JavaAst, root: JavaId, name: &str) -> bool {
4158    let mut stack = vec![root];
4159    while let Some(n) = stack.pop() {
4160        if ast.kind(n) == NodeKind::Call {
4161            let callee = ast.children(n)[0];
4162            if ast.var_name(callee) == Some(name) {
4163                return true;
4164            }
4165            // 成员调用名也算(Boolean.getBoolean——jdk-sources TCPEndpoint
4166            // 抓获:库方法与本地方法恰好同名时,匹配器按
4167            // "大写接收方.方法名" 也会命中 → 自我乒乓无限克隆 → 爆栈;
4168            // 保守拒绝(误拒仅损失一次内联机会)
4169            if let NodeData::Member { name: m } = ast.data(callee) {
4170                if ast.sn(*m) == name {
4171                    return true;
4172                }
4173            }
4174        }
4175        for &c in ast.children(n) {
4176            stack.push(c);
4177        }
4178    }
4179    false
4180}
4181
4182// ---------------------------------------------------------------------------
4183// 常量字段下标折叠:T[i](T 为字面量数组常量字段)→ 元素字面量
4184// ---------------------------------------------------------------------------
4185
4186pub struct StaticArrayIndexFold;
4187
4188impl Rule<JavaAst> for StaticArrayIndexFold {
4189    fn name(&self) -> &'static str {
4190        "static_array_index_fold"
4191    }
4192    fn kinds(&self) -> &'static [NodeKind] {
4193        &[NodeKind::Index]
4194    }
4195    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4196        let lang = ctx.lang;
4197        if lang.kind(id) != NodeKind::Index {
4198            return None;
4199        }
4200        let ch = lang.children(id).to_vec();
4201        if lang.kind(ch[0]) != NodeKind::VarRef {
4202            return None;
4203        }
4204        let name = lang.var_name(ch[0])?.to_string();
4205        let idx = lang.literal(ch[1]).and_then(|x| x.as_int())?;
4206        let init = *lang.const_fields.get(&name)?;
4207        let arr = match lang.kind(init) {
4208            NodeKind::ArrayLit => init,
4209            NodeKind::NewArray => {
4210                let ic = lang.children(init).to_vec();
4211                if ic.len() == 1 && lang.kind(ic[0]) == NodeKind::ArrayLit {
4212                    ic[0]
4213                } else {
4214                    return None;
4215                }
4216            }
4217            _ => return None,
4218        };
4219        let elems = lang.children(arr).to_vec();
4220        if idx < 0 || idx >= elems.len() as i64 {
4221            return None;
4222        }
4223        let elem = lang.copy_subtree(elems[idx as usize]);
4224        Some(Edit::Replace {
4225            target: id,
4226            with: elem,
4227        })
4228    }
4229}
4230
4231// ---------------------------------------------------------------------------
4232// 常量字段标量传播:static final 字面量字段的 VarRef 读点 → 字面量。
4233// 跨方法/跨语句常量传播(bd.java:a="SHA-512" → getInstance(a) 处内联)。
4234// 守卫:
4235//   - assign 目标(含复合/++/--)跳过:clinit 的原赋值 lhs 不能替换;
4236//   - Call 的 callee 跳过:字段与方法是不同命名空间(bd.java 的字段 a
4237//     与静态方法 a 共存,a(c, b) 是调用不是字段读);
4238//   - const_scalars 只收声明类型与字面量种类精确匹配的标量(收集期
4239//     已守卫过载解析;数组常量只走下标折叠)。
4240// 语义:intern() 常量(bd 的 a/j 即 new String(...).intern() 材料)
4241// 与同类字面量共享常量池实例 → == 身份比较亦保持。
4242// ---------------------------------------------------------------------------
4243
4244// ---------------------------------------------------------------------------
4245// 空私有方法 no-op 调用删除:`e(c, b);` —— e 是空体 private 方法(收集
4246// 期元数隔离已守卫过载与 varargs)→ 调用唯一可观察效果是实参求值。
4247/// 实参效应 ≤ MayRead(VarRef/字面量/纯算术——字段读不抛不写;数组读
4248// (MayThrow)/调用(Unknown)/赋值(MayWrite)一律保留)。
4249// opt-in 生效:表由 simplify_unit 在 --dead-code 下填充。
4250// ---------------------------------------------------------------------------
4251
4252// ---------------------------------------------------------------------------
4253// 常量字段标量传播:static final 字面量字段的 VarRef 读点 → 字面量。
4254// 跨方法/跨语句常量传播(bd.java:a="SHA-512" → getInstance(a) 处内联)。
4255// 守卫:
4256//   - assign 目标(含复合/++/--)跳过:clinit 的原赋值 lhs 不能替换;
4257//   - Call 的 callee 跳过:字段与方法是不同命名空间(bd.java 的字段 a
4258//     与静态方法 a 共存,a(c, b) 是调用不是字段读);
4259//   - const_scalars 只收声明类型与字面量种类精确匹配的标量(收集期
4260//     已守卫过载解析;数组常量只走下标折叠)。
4261// 语义:intern() 常量(bd 的 a/j 即 new String(...).intern() 材料)
4262// 与同类字面量共享常量池实例 → == 身份比较亦保持。
4263// ---------------------------------------------------------------------------
4264
4265// ---------------------------------------------------------------------------
4266// 尾随赋值即返回/抛出(P2 简化空间挖掘):`v = expr; return v;` →
4267// `return expr;`(throw 变体同)。v 为局部(字段回写惯用形排除);
4268// 相邻约束(赋值与 return 之间无语句)+ return 之后语句零 v 引用
4269//(死代码里的引用会因赋值消失而失定赋值)。RHS 自引用惯用形
4270// (result = result + x; return result;)安全:折叠读旧值。
4271// 相邻性同时排除捕获场景:合法 Java 中 lambda 捕获要求 effectively
4272// final——本赋值之后的捕获点要么违反相邻约束要么在死代码里。
4273// ---------------------------------------------------------------------------
4274
4275// ---------------------------------------------------------------------------
4276// 循环体末尾的 `if (c) { continue; }` 是 no-op(continue 跳回循环步进/
4277// 条件——与体末尾落空同一位置)。仅当 if 是体的**最后一条**语句且无
4278// else(ba.java 形态:反编译 if 链的尾残留)。continue 无副作用,
4279// 删除整条 if 与落空等价。
4280// ---------------------------------------------------------------------------
4281
4282pub struct TrailingIfContinue;
4283
4284impl Rule<JavaAst> for TrailingIfContinue {
4285    fn name(&self) -> &'static str {
4286        "trailing_if_continue"
4287    }
4288    fn kinds(&self) -> &'static [NodeKind] {
4289        &[NodeKind::If]
4290    }
4291    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4292        let parent = ctx.parent(id);
4293        let grandparent = parent.and_then(|p| ctx.parent(p));
4294        let lang = ctx.lang;
4295        if lang.kind(id) != NodeKind::If {
4296            return None;
4297        }
4298        let ch = lang.children(id).to_vec();
4299        // 恰两分支(cond, then——无 else)
4300        if ch.len() != 2 {
4301            return None;
4302        }
4303        let then = ch[1];
4304        if lang.kind(then) != NodeKind::Block {
4305            return None;
4306        }
4307        let tbody = lang.children(then).to_vec();
4308        if tbody.len() != 1 || lang.kind(tbody[0]) != NodeKind::Continue {
4309            return None;
4310        }
4311        // 无标签 continue(带标签的跳法不同)
4312        if !matches!(lang.data(tbody[0]), NodeData::Continue { label: None }) {
4313            return None;
4314        }
4315        // 父是块且本 if 是最后一条语句;父块是循环体(While/For/ForEach
4316        // 的最后一个孩子)
4317        let p = parent?;
4318        if lang.kind(p) != NodeKind::Block {
4319            return None;
4320        }
4321        let pch = lang.children(p).to_vec();
4322        if pch.last() != Some(&id) {
4323            return None;
4324        }
4325        // 祖父是循环(While/For/ForEach 的体)
4326        let gp = grandparent?;
4327        match lang.kind(gp) {
4328            NodeKind::While | NodeKind::For | NodeKind::ForEach | NodeKind::DoWhile => {
4329                let gch = lang.children(gp).to_vec();
4330                if gch.last() == Some(&p) {
4331                    Some(Edit::Delete { node: id })
4332                } else {
4333                    None
4334                }
4335            }
4336            _ => None,
4337        }
4338    }
4339}
4340
4341pub struct TrailingAssignReturn;
4342
4343impl Rule<JavaAst> for TrailingAssignReturn {
4344    fn name(&self) -> &'static str {
4345        "trailing_assign_return"
4346    }
4347    fn kinds(&self) -> &'static [NodeKind] {
4348        &[NodeKind::Assign]
4349    }
4350    // 节点数持平(Assign+Return 2 语句 → 1 语句)——按节点计数不减,
4351    // 价值在行数与后续连锁(if_to_ternary 等以 return 形态收敛)
4352    fn structural(&self) -> bool {
4353        true
4354    }
4355    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4356        let parent = ctx.parent(id);
4357        let grandparent = parent.and_then(|p| ctx.parent(p));
4358        let lang = ctx.lang;
4359        // 简单赋值(复合赋值不折)
4360        if !matches!(lang.data(id), NodeData::Assign { op: None }) {
4361            return None;
4362        }
4363        let ch = lang.children(id).to_vec();
4364        if ch.len() != 2 {
4365            return None;
4366        }
4367        let (target, value) = (ch[0], ch[1]);
4368        // 目标:局部(字段/参数回写惯用形排除——getter 回写是语义性的)
4369        if lang.kind(target) != NodeKind::VarRef || !lang.is_local_var(target) {
4370            return None;
4371        }
4372        let name = lang.var_name(target)?.to_string();
4373        // 赋值语句的父块(语句级:ExprStmt 包裹或裸 Assign)
4374        let parent = parent?;
4375        let stmt = if lang.kind(parent) == NodeKind::ExprStmt {
4376            parent
4377        } else if lang.kind(parent) == NodeKind::Block {
4378            id
4379        } else {
4380            return None;
4381        };
4382        let block = if lang.kind(parent) == NodeKind::ExprStmt {
4383            grandparent?
4384        } else {
4385            parent
4386        };
4387        if lang.kind(block) != NodeKind::Block {
4388            return None;
4389        }
4390        let stmts = lang.children(block).to_vec();
4391        let idx = stmts.iter().position(|&x| x == stmt)?;
4392        // 相邻下一条:return v / throw v
4393        let next = *stmts.get(idx + 1)?;
4394        let (flow_val, is_return) = match lang.kind(next) {
4395            NodeKind::Return => (lang.children(next).first().copied(), true),
4396            NodeKind::Throw => (lang.children(next).first().copied(), false),
4397            _ => return None,
4398        };
4399        let flow_val = flow_val?;
4400        if lang.kind(flow_val) != NodeKind::VarRef || lang.var_name(flow_val) != Some(name.as_str()) {
4401            return None;
4402        }
4403        // return 之后语句零 v 引用(死代码里的引用会因赋值消失失去
4404        // 定赋值而不可编译)
4405        for &s in &stmts[idx + 2..] {
4406            if cure_engine::analysis::subtree_contains(lang, s, |n| {
4407                lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name.as_str())
4408            }) {
4409                return None;
4410            }
4411        }
4412        // 折叠:下一条流语句替换为 return/throw expr;赋值语句删除
4413        let new_flow = if is_return {
4414            lang.ret(Some(value))
4415        } else {
4416            lang.throw(value)
4417        };
4418        Some(Edit::Multi(vec![
4419            Edit::Replace { target: next, with: new_flow },
4420            Edit::Delete { node: stmt },
4421        ]))
4422    }
4423}
4424
4425pub struct ConstFieldPropagate;
4426
4427impl Rule<JavaAst> for ConstFieldPropagate {
4428    fn name(&self) -> &'static str {
4429        "const_field_propagate"
4430    }
4431    fn kinds(&self) -> &'static [NodeKind] {
4432        &[NodeKind::VarRef]
4433    }
4434    // 结构性规则:VarRef→Literal 节点数持平(成本模型不增不减)——
4435    // 价值在语义透明(读点直接可读),非节点缩减
4436    fn structural(&self) -> bool {
4437        true
4438    }
4439    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4440        let parent = ctx.parent(id);
4441        let _grandparent = parent.and_then(|p| ctx.parent(p));
4442        let lang = ctx.lang;
4443        if lang.kind(id) != NodeKind::VarRef {
4444            return None;
4445        }
4446        let name = lang.var_name(id)?.to_string();
4447        if !lang.const_scalars.contains(&name) {
4448            return None;
4449        }
4450        if let Some(p) = parent {
4451            let is_first = lang.children(p).first() == Some(&id);
4452            match lang.kind(p) {
4453                // 赋值目标(含复合赋值):保留原赋值
4454                NodeKind::Assign if is_first => return None,
4455                NodeKind::Unary if is_first => {
4456                    if let NodeData::Unary { op } = lang.data(p) {
4457                        if op.is_incdec() {
4458                            return None;
4459                        }
4460                    }
4461                }
4462                // 裸名调用 callee:方法名,不是字段读
4463                NodeKind::Call if is_first => return None,
4464                _ => {}
4465            }
4466        }
4467        let init = *lang.const_fields.get(&name)?;
4468        let lit = lang.copy_subtree(init);
4469        Some(Edit::Replace { target: id, with: lit })
4470    }
4471}
4472
4473pub struct NoopPrivateCall;
4474
4475impl Rule<JavaAst> for NoopPrivateCall {
4476    fn name(&self) -> &'static str {
4477        "noop_private_call"
4478    }
4479    fn kinds(&self) -> &'static [NodeKind] {
4480        &[NodeKind::ExprStmt]
4481    }
4482    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4483        let lang = ctx.lang;
4484        if lang.kind(id) != NodeKind::ExprStmt {
4485            return None;
4486        }
4487        let ch = lang.children(id).to_vec();
4488        if ch.len() != 1 || lang.kind(ch[0]) != NodeKind::Call {
4489            return None;
4490        }
4491        let call = ch[0];
4492        let cch = lang.children(call).to_vec();
4493        if cch.is_empty() || lang.kind(cch[0]) != NodeKind::VarRef {
4494            return None; // 限定名调用(A.m / this.m)是别的方法解析域
4495        }
4496        let name = lang.var_name(cch[0])?.to_string();
4497        let arity = cch.len() - 1;
4498        let cands = lang.noop_private_methods.get(&name)?.get(&arity)?;
4499        if cands.is_empty() {
4500            return None;
4501        }
4502        // 类型适用性消解:实参静态类型(局部 var_type / 字段 field_types /
4503        // 字面量种类)对每个候选形参表判定;未知实参 → 该候选视为适用
4504        //(不能证不适用——保守)。唯一适用者且 noop → 删除语句。
4505        let arg_types: Vec<Option<JType>> =
4506            cch[1..].iter().map(|&a| static_arg_type(lang, a)).collect();
4507        let mut applicable_noop = false;
4508        let mut applicable_count = 0usize;
4509        for c in cands {
4510            let ok = c
4511                .params
4512                .iter()
4513                .zip(arg_types.iter())
4514                .all(|(pt, at)| arg_applicable(at.as_ref(), pt));
4515            if ok {
4516                applicable_count += 1;
4517                applicable_noop = c.noop;
4518            }
4519        }
4520        if applicable_count != 1 || !applicable_noop {
4521            return None;
4522        }
4523        // 实参效应 ≤ MayRead(VarRef/字面量/纯算术——字段读不抛不写)
4524        for &arg in &cch[1..] {
4525            if lang.effect(arg) > Effect::MayRead {
4526                return None;
4527            }
4528        }
4529        Some(Edit::Delete { node: id })
4530    }
4531}
4532
4533/// 实参静态类型(判定过载适用性用):局部/参数走 var_type 解析,字段
4534/// 走 field_types 表,字面量按种类。null → None(任何引用形参都可能——
4535/// 全候选适用,保守)。其余表达式 → None。
4536fn static_arg_type(lang: &JavaAst, arg: JavaId) -> Option<JType> {
4537    match lang.kind(arg) {
4538        NodeKind::Literal => match lang.literal(arg)? {
4539            LitRef::Str(_) => Some(JType::Ref("String".to_string())),
4540            LitRef::Char(_) => Some(JType::Char),
4541            LitRef::Bool(_) => Some(JType::Bool),
4542            LitRef::Int(_) => Some(JType::Int),
4543            LitRef::Long(_) => Some(JType::Long),
4544            LitRef::Float(_) => Some(JType::Float),
4545            LitRef::Double(_) => Some(JType::Double),
4546            LitRef::Null => None,
4547        },
4548        NodeKind::VarRef => {
4549            let nm = lang.var_name(arg)?;
4550            lang.var_type(arg)
4551                .cloned()
4552                .or_else(|| lang.field_types.get(nm).cloned())
4553        }
4554        _ => None,
4555    }
4556}
4557
4558/// 实参类型对形参的适用性(保守方向:不能证不适用 → true)。
4559/// JLS 过载解析的保守近似:精确简单名匹配 + Object 通配;装箱/拓宽
4560/// 判不适用(配合「唯一适用」判定仍安全:装箱场景要么 0 适用要么
4561/// 精确者胜出)。
4562fn arg_applicable(at: Option<&JType>, pt: &JType) -> bool {
4563    let Some(a) = at else { return true };
4564    match pt {
4565        JType::Ref(p) => {
4566            let pbase = p.split('<').next().unwrap_or(p).trim();
4567            pbase == "Object"
4568                || pbase == "java.lang.Object"
4569                || match a {
4570                    JType::Ref(arend) => {
4571                        let abase = arend.split('<').next().unwrap_or(arend).trim();
4572                        abase == pbase
4573                    }
4574                    _ => false,
4575                }
4576        }
4577        JType::Array(_) => false, // 简单名/字面量实参不适用数组形参(null → None 已兜底)
4578        prim => match a {
4579            p2 => std::mem::discriminant(p2) == std::mem::discriminant(prim),
4580        },
4581    }
4582}
4583
4584// ---------------------------------------------------------------------------
4585// 常量实参方法内联(解密 helper):d(0) → 方法体(参数→实参字面量替换)。
4586// 语义恒安全:单 return 表达式在调用点求值一次,两种形态一致;
4587// 自递归方法拒绝;实参须全为字面量。
4588// 【结构性规则】豁免成本门槛:内联暂时变贵、由后续常量折叠回本。
4589// 终止性:守卫"体内不含任何内联名的裸调用"——每次内联严格消费一个
4590// 此类调用点且不引入新的同类节点。
4591// ---------------------------------------------------------------------------
4592
4593pub struct ConstMethodInline;
4594
4595impl Rule<JavaAst> for ConstMethodInline {
4596    fn name(&self) -> &'static str {
4597        "const_method_inline"
4598    }
4599    fn kinds(&self) -> &'static [NodeKind] {
4600        &[NodeKind::Call]
4601    }
4602    fn structural(&self) -> bool {
4603        true
4604    }
4605    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4606        // 语句位标志先取(ctx.lang 借用后 ctx 部分移动——R13 教训)
4607        let at_stmt_pos = ctx
4608            .parent(id)
4609            .map(|p| ctx.lang.kind(p) == NodeKind::ExprStmt)
4610            .unwrap_or(false);
4611        let lang = ctx.lang;
4612        if lang.kind(id) != NodeKind::Call {
4613            return None;
4614        }
4615        let ch = lang.children(id).to_vec();
4616        let method_name = match lang.data(ch[0]) {
4617            NodeData::VarRef { name } => *name,
4618            NodeData::Member { name } => {
4619                let recv = lang.children(ch[0])[0];
4620                match lang.var_name(recv) {
4621                    Some(r) if r == "this" || r.chars().next().is_some_and(|c| c.is_uppercase()) => {
4622                        *name
4623                    }
4624                    _ => return None,
4625                }
4626            }
4627            _ => return None,
4628        };
4629        let entry = lang.inline_methods.get(lang.sn(method_name)).cloned()?;
4630        let (params, body_expr) = entry;
4631        // 终止守卫:方法体内不得含任何【内联名】的裸调用
4632        {
4633            let names: Vec<String> = lang.inline_methods.keys().cloned().collect();
4634            for nm in &names {
4635                if subtree_calls_self(lang, body_expr, nm) {
4636                    return None;
4637                }
4638            }
4639        }
4640        let args = ch[1..].to_vec();
4641        if args.len() != params.len() || params.is_empty() {
4642            return None;
4643        }
4644        for &a in &args {
4645            lang.literal(a)?;
4646        }
4647        let map: std::collections::HashMap<String, JavaId> = params
4648            .iter()
4649            .zip(args.iter())
4650            .map(|(p, &a)| (p.clone(), a))
4651            .collect();
4652        let with = copy_subst(lang, body_expr, &map);
4653        // **语句位形态守卫**(ddc/deepseek cs8 抓获:`e("KType");` 的
4654        // 单 return 体内联成 `mk(...).a;`——字段访问不是合法表达式语句
4655        //(JLS 14.8 仅赋值/自增自减/调用/new),与 R12 foreach 的 e; 同
4656        // 族)。调用在裸 ExprStmt 位(结果弃置)时,替换表达式必须是
4657        // Call/New——否则保留原调用
4658        if at_stmt_pos {
4659            match lang.kind(with) {
4660                NodeKind::Call | NodeKind::New => {}
4661                _ => return None,
4662            }
4663        }
4664        Some(Edit::Replace {
4665            target: id,
4666            with,
4667        })
4668    }
4669}
4670
4671/// 深拷贝 + VarRef 参数替换。
4672fn copy_subst(
4673    lang: &mut JavaAst,
4674    node: JavaId,
4675    map: &std::collections::HashMap<String, JavaId>,
4676) -> JavaId {
4677    if let NodeData::VarRef { name } = lang.data(node) {
4678        if let Some(&repl) = map.get(lang.sn(*name)) {
4679            return lang.copy_subtree(repl);
4680        }
4681    }
4682    let children: Vec<JavaId> = lang.children(node).to_vec();
4683    let new_children: Vec<JavaId> = children
4684        .iter()
4685        .map(|&c| copy_subst(lang, c, map))
4686        .collect();
4687    lang.clone_node(node, new_children)
4688}
4689
4690
4691// ---------------------------------------------------------------------------
4692// try-with-resources 还原(twr_recover):两种反编译器直出形态。
4693//
4694// family B(ddc:javac→d8 字节码的紧凑渲染):
4695//   R r = new R(...);            // 资源(new——无卫语句形态下唯一非空证明)
4696//   try { B }
4697//   catch (Throwable t) {
4698//       try { r.close(); }
4699//       catch (Throwable sup) { t.addSuppressed(sup); }
4700//       throw t;
4701//   }
4702//   r.close();                   // 正常路径尾部直调
4703//   → try (R r = new R(...)) { B }
4704//
4705// family A(javac 源级翻译;CFR/ProGuard 常见):
4706//   R r = init; Throwable primary = null;
4707//   try { B }
4708//   catch (Throwable t) { primary = t; throw t; }
4709//   finally {
4710//       if (r != null) {
4711//           if (primary != null) {
4712//               try { r.close(); } catch (Throwable sup) { primary.addSuppressed(sup); }
4713//           } else { r.close(); }
4714//       }
4715//   }
4716//   → try (R r = init) { B }      // 含 null 资源跳过 close 的路径(卫语句保证)
4717//
4718// 多资源:嵌套形态由不动点逐层还原(内层先→外层 try 体含 try(res)——
4719// JLS 上嵌套 TWR ≡ 多资源 TWR,无需合并)。守卫:r/primary/t/sup 的全部
4720// 引用必须落在模式消费的子树内(否则不还原);同名遮蔽一律拒绝。
4721// ---------------------------------------------------------------------------
4722
4723pub struct TwrRecover;
4724
4725fn twr_is_throwable(ty_raw: &str) -> bool {
4726    ty_raw == "Throwable" || ty_raw == "java.lang.Throwable"
4727}
4728
4729/// 表达式语句解包:ExprStmt{e} → e(赋值/调用等语句位置)。
4730fn unwrap_expr_stmt(lang: &JavaAst, n: JavaId) -> JavaId {
4731    if lang.kind(n) == NodeKind::ExprStmt {
4732        if let Some(&e) = lang.children(n).first() {
4733            return e;
4734        }
4735    }
4736    n
4737}
4738
4739/// 语句序列(Block 展开;单语句原样)。
4740fn twr_stmts(lang: &JavaAst, n: JavaId) -> Vec<JavaId> {
4741    if lang.kind(n) == NodeKind::Block {
4742        lang.children(n).to_vec()
4743    } else {
4744        vec![n]
4745    }
4746}
4747
4748/// `r.close()` 调用表达式(ExprStmt 解包后)。
4749fn twr_is_close_call(lang: &JavaAst, e: JavaId, r: &str) -> bool {
4750    if lang.kind(e) != NodeKind::Call {
4751        return false;
4752    }
4753    let ch = lang.children(e);
4754    if ch.len() != 1 {
4755        return false;
4756    }
4757    match lang.data(ch[0]) {
4758        NodeData::Member { name } if lang.sn(*name) == "close" => {
4759            let Some(&recv) = lang.children(ch[0]).first() else {
4760                return false;
4761            };
4762            lang.kind(recv) == NodeKind::VarRef && lang.var_name(recv) == Some(r)
4763        }
4764        _ => false,
4765    }
4766}
4767
4768fn twr_is_close_stmt(lang: &JavaAst, n: JavaId, r: &str) -> bool {
4769    if lang.kind(n) != NodeKind::ExprStmt {
4770        return false;
4771    }
4772    lang.children(n)
4773        .first()
4774        .is_some_and(|&e| twr_is_close_call(lang, e, r))
4775}
4776
4777/// `primary.addSuppressed(sup)` 语句。
4778fn twr_is_add_suppressed(lang: &JavaAst, n: JavaId, primary: &str, sup: &str) -> bool {
4779    if lang.kind(n) != NodeKind::ExprStmt {
4780        return false;
4781    }
4782    let Some(&e) = lang.children(n).first() else {
4783        return false;
4784    };
4785    if lang.kind(e) != NodeKind::Call {
4786        return false;
4787    }
4788    let ch = lang.children(e);
4789    if ch.len() != 2 {
4790        return false;
4791    }
4792    match lang.data(ch[0]) {
4793        NodeData::Member { name } if lang.sn(*name) == "addSuppressed" => {
4794            let Some(&recv) = lang.children(ch[0]).first() else {
4795                return false;
4796            };
4797            lang.kind(recv) == NodeKind::VarRef
4798                && lang.var_name(recv) == Some(primary)
4799                && lang.kind(ch[1]) == NodeKind::VarRef
4800                && lang.var_name(ch[1]) == Some(sup)
4801        }
4802        _ => false,
4803    }
4804}
4805
4806fn node_contains(lang: &JavaAst, root: JavaId, target: JavaId) -> bool {
4807    let mut stack = vec![root];
4808    while let Some(n) = stack.pop() {
4809        if n == target {
4810            return true;
4811        }
4812        stack.extend(lang.children(n).iter().copied());
4813    }
4814    false
4815}
4816
4817/// root 内存在 name 的引用且不在任何 allowed 子树内(模式外的逃逸引用)。
4818fn refs_escape(lang: &JavaAst, root: JavaId, name: &str, allowed: &[JavaId]) -> bool {
4819    let mut stack = vec![root];
4820    while let Some(n) = stack.pop() {
4821        if lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name) {
4822            let ok = allowed
4823                .iter()
4824                .any(|&a| a == n || node_contains(lang, a, n));
4825            if !ok {
4826                return true;
4827            }
4828        }
4829        if lang.kind(n) == NodeKind::VarDecl {
4830            if let Some(nm) = lang.var_name(n) {
4831                if nm == name && !allowed.contains(&n) {
4832                    // 同名再声明(遮蔽风险)→ 拒绝
4833                    return true;
4834                }
4835            }
4836        }
4837        stack.extend(lang.children(n).iter().copied());
4838    }
4839    false
4840}
4841
4842/// Try{try 块, 单 catch(Throwable, sup), 无 finally} 且 catch 体 =
4843/// [addSuppressed(primary, sup)]、try 体 = [r.close()]。
4844/// 返回 (r 名, sup 名)。primary 传闭包外确定。
4845fn twr_close_with_suppressed(
4846    lang: &JavaAst,
4847    try_node: JavaId,
4848    primary: &str,
4849) -> Option<(String, String)> {
4850    let ch = lang.children(try_node).to_vec();
4851    if ch.len() != 2 || lang.kind(ch[0]) != NodeKind::Block {
4852        return None;
4853    }
4854    let NodeData::Catch { ty_raw, name: sup } = lang.data(ch[1]) else {
4855        return None;
4856    };
4857    if !twr_is_throwable(ty_raw) {
4858        return None;
4859    }
4860    let sup = sup.clone();
4861    let catch_stmts = twr_stmts(lang, *lang.children(ch[1]).first()?);
4862    if catch_stmts.len() != 1 || !twr_is_add_suppressed(lang, catch_stmts[0], primary, &sup) {
4863        return None;
4864    }
4865    let close_stmts = twr_stmts(lang, ch[0]);
4866    if close_stmts.len() != 1 {
4867        return None;
4868    }
4869    if lang.kind(close_stmts[0]) != NodeKind::ExprStmt {
4870        return None;
4871    }
4872    let e = *lang.children(close_stmts[0]).first()?;
4873    let cc = lang.children(e);
4874    if cc.len() != 1 {
4875        return None;
4876    }
4877    let NodeData::Member { name } = lang.data(cc[0]) else {
4878        return None;
4879    };
4880    if lang.sn(*name) != "close" {
4881        return None;
4882    }
4883    let recv = *lang.children(cc[0]).first()?;
4884    if lang.kind(recv) != NodeKind::VarRef {
4885        return None;
4886    }
4887    Some((lang.var_name(recv)?.to_string(), sup))
4888}
4889
4890impl Rule<JavaAst> for TwrRecover {
4891    fn name(&self) -> &'static str {
4892        "twr_recover"
4893    }
4894    fn kinds(&self) -> &'static [NodeKind] {
4895        &[NodeKind::Try]
4896    }
4897    fn structural(&self) -> bool {
4898        true
4899    }
4900    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4901        // 先取 ctx 查询(lang 的 &mut 移出后 ctx 不可再用)
4902        let (parent, idx, walk_root) = (ctx.parent(id)?, ctx.index(id)?, ctx.root());
4903        let lang = ctx.lang;
4904        if lang.kind(id) != NodeKind::Try {
4905            return None;
4906        }
4907        let ch = lang.children(id).to_vec();
4908        // 资源已存在的(嵌套已还原)不再匹配
4909        let try_idx = ch
4910            .iter()
4911            .position(|&c| lang.kind(c) == NodeKind::Block)?;
4912        if try_idx != 0 {
4913            return None;
4914        }
4915        let try_block = ch[0];
4916        if lang.kind(parent) != NodeKind::Block {
4917            return None;
4918        }
4919        let pch = lang.children(parent).to_vec();
4920
4921        // ===== family B:[decl_r, try{B}catch, close] =====
4922        if ch.len() == 2 {
4923            if let NodeData::Catch { ty_raw, name: t } = lang.data(ch[1]) {
4924                if twr_is_throwable(ty_raw) {
4925                    let t = t.clone();
4926                    let catch_stmts = twr_stmts(lang, *lang.children(ch[1]).first()?);
4927                    if catch_stmts.len() == 2 {
4928                        // [Try{close,sup}, throw t]
4929                        let (inner_try, throw_stmt) = (catch_stmts[0], catch_stmts[1]);
4930                        let throw_ok = lang.kind(throw_stmt) == NodeKind::Throw
4931                            && lang
4932                                .children(throw_stmt)
4933                                .first()
4934                                .is_some_and(|&x| {
4935                                    lang.kind(x) == NodeKind::VarRef
4936                                        && lang.var_name(x) == Some(t.as_str())
4937                                });
4938                        if throw_ok && lang.kind(inner_try) == NodeKind::Try {
4939                            if let Some((r, sup)) =
4940                                twr_close_with_suppressed(lang, inner_try, t.as_str())
4941                            {
4942                                if idx >= 1 && idx + 1 < pch.len() {
4943                                    let decl_r = pch[idx - 1];
4944                                    let trailing = pch[idx + 1];
4945                                    if let NodeData::VarDecl { name, ty, .. } = lang.data(decl_r) {
4946                                        if name == &r {
4947                                            let (name, ty) = (name.clone(), ty.clone());
4948                                            let init =
4949                                                lang.children(decl_r).first().copied();
4950                                            if let Some(init) = init {
4951                                                // 无卫语句形态:init 须为 new(非空证明)
4952                                                let provably_new = lang.kind(init)
4953                                                    == NodeKind::New;
4954                                                let close_ok =
4955                                                    twr_is_close_stmt(lang, trailing, &r);
4956                                                if provably_new && close_ok {
4957                                                    // 引用逃逸扫描
4958                                                    let root = walk_root;
4959                                                    let r_ok = !refs_escape(
4960                                                        lang,
4961                                                        root,
4962                                                        &r,
4963                                                        &[
4964                                                            decl_r,
4965                                                            init,
4966                                                            *lang
4967                                                                .children(inner_try)
4968                                                                .first()
4969                                                                .unwrap_or(&inner_try),
4970                                                            trailing,
4971                                                            try_block,
4972                                                        ],
4973                                                    );
4974                                                    let t_ok = !refs_escape(
4975                                                        lang, root, &t,
4976                                                        &[throw_stmt, ch[1]],
4977                                                    );
4978                                                    let sup_ok = !refs_escape(
4979                                                        lang, root, &sup,
4980                                                        &[ch[1]],
4981                                                    );
4982                                                    if r_ok && t_ok && sup_ok {
4983                                                        let init_copy =
4984                                                            lang.copy_subtree(init);
4985                                                        let res = lang.var_decl(
4986                                                            &name, ty,
4987                                                            Some(init_copy),
4988                                                        );
4989                                                        let new_try = lang.try_(
4990                                                            vec![res],
4991                                                            try_block,
4992                                                            Vec::new(),
4993                                                            None,
4994                                                        );
4995                                                        return Some(Edit::Splice {
4996                                                            node: parent,
4997                                                            index: idx - 1,
4998                                                            remove: 3,
4999                                                            insert: vec![new_try],
5000                                                        });
5001                                                    }
5002                                                }
5003                                            }
5004                                        }
5005                                    }
5006                                }
5007                            }
5008                        }
5009                    }
5010                }
5011            }
5012        }
5013
5014        // ===== family A:[decl_r, decl_primary, try{B}catch{primary=t;throw}finally{卫语句}] =====
5015        if ch.len() == 3 {
5016            let NodeData::Catch { ty_raw, name: t } = lang.data(ch[1]) else {
5017                return None;
5018            };
5019            if !twr_is_throwable(ty_raw) {
5020                return None;
5021            }
5022            let t = t.clone();
5023            // finally 块
5024            if lang.kind(ch[2]) != NodeKind::Block {
5025                return None;
5026            }
5027            let fin_stmts = twr_stmts(lang, ch[2]);
5028            if fin_stmts.len() != 1 || lang.kind(fin_stmts[0]) != NodeKind::If {
5029                return None;
5030            }
5031            // catch 体 = [primary = t, throw t]
5032            let catch_stmts = twr_stmts(lang, *lang.children(ch[1]).first()?);
5033            if catch_stmts.len() != 2 {
5034                return None;
5035            }
5036            let (assign_stmt, throw_stmt) = (catch_stmts[0], catch_stmts[1]);
5037            let assign_expr = unwrap_expr_stmt(lang, assign_stmt);
5038            let NodeData::Assign { op: None } = lang.data(assign_expr) else {
5039                return None;
5040            };
5041            let as_ch = lang.children(assign_expr);
5042            if as_ch.len() != 2 {
5043                return None;
5044            }
5045            let primary = match lang.data(as_ch[0]) {
5046                NodeData::VarRef { name } => lang.sn(*name).to_string(),
5047                _ => return None,
5048            };
5049            let assign_ok = lang.kind(as_ch[1]) == NodeKind::VarRef
5050                && lang.var_name(as_ch[1]) == Some(t.as_str())
5051                && lang.kind(as_ch[0]) == NodeKind::VarRef
5052                && lang.var_name(as_ch[0]) == Some(primary.as_str());
5053            let throw_ok = lang.kind(throw_stmt) == NodeKind::Throw
5054                && lang.children(throw_stmt)
5055                    .first()
5056                    .is_some_and(|&x| {
5057                        lang.kind(x) == NodeKind::VarRef
5058                            && lang.var_name(x) == Some(t.as_str())
5059                    });
5060            if !assign_ok || !throw_ok {
5061                return None;
5062            }
5063            // 卫语句:if (r != null) { if (primary != null) { try{r.close()}catch{sup} } else { r.close() } }
5064            let if1 = fin_stmts[0];
5065            let i1 = lang.children(if1).to_vec();
5066            if i1.len() < 2 {
5067                return None;
5068            }
5069            let cond1 = i1[0];
5070            let NodeData::Binary { op: BinOp::Ne } = lang.data(cond1) else {
5071                return None;
5072            };
5073            let c1 = lang.children(cond1);
5074            let r = match lang.data(c1[0]) {
5075                NodeData::VarRef { name } if lang.literal(c1[1]) == Some(LitRef::Null) => {
5076                    lang.sn(*name).to_string()
5077                }
5078                _ => {
5079                    return None;
5080                }
5081            };
5082            let then1 = i1[1];
5083            let inner_stmts = twr_stmts(lang, then1);
5084            if inner_stmts.len() != 1 || lang.kind(inner_stmts[0]) != NodeKind::If {
5085                return None;
5086            }
5087            let if2 = inner_stmts[0];
5088            let i2 = lang.children(if2).to_vec();
5089            if i2.len() != 3 {
5090                return None;
5091            }
5092            let NodeData::Binary { op: BinOp::Ne } = lang.data(i2[0]) else {
5093                return None;
5094            };
5095            let c2 = lang.children(i2[0]);
5096            let primary_ok = lang.kind(c2[0]) == NodeKind::VarRef
5097                && lang.var_name(c2[0]) == Some(primary.as_str())
5098                && lang.literal(c2[1]) == Some(LitRef::Null);
5099            if !primary_ok {
5100                return None;
5101            }
5102            let (then2, els2) = (i2[1], i2[2]);
5103            // then2: Try{[r.close()], catch{primary.addSuppressed(sup)}}
5104            let t2_stmts = twr_stmts(lang, then2);
5105            if t2_stmts.len() != 1 || lang.kind(t2_stmts[0]) != NodeKind::Try {
5106                return None;
5107            }
5108            let Some((r2, sup)) =
5109                twr_close_with_suppressed(lang, t2_stmts[0], primary.as_str())
5110            else {
5111                return None;
5112            };
5113            if r2 != r {
5114                return None;
5115            }
5116            // els2: [r.close()]
5117            let e2_stmts = twr_stmts(lang, els2);
5118            if e2_stmts.len() != 1 || !twr_is_close_stmt(lang, e2_stmts[0], &r) {
5119                return None;
5120            }
5121            // 父块三连:decl_r, decl_primary, try
5122            if idx < 2 {
5123                return None;
5124            }
5125            let decl_r = pch[idx - 2];
5126            let decl_primary = pch[idx - 1];
5127            let (r_decl, primary_decl) = match (lang.data(decl_r), lang.data(decl_primary)) {
5128                (
5129                    NodeData::VarDecl { name: rn, .. },
5130                    NodeData::VarDecl { name: pn, .. },
5131                ) => (rn.clone(), pn.clone()),
5132                _ => return None,
5133            };
5134            if r_decl != r || primary_decl != primary {
5135                return None;
5136            }
5137            // primary 初始化为 null
5138            let pinit_null = lang
5139                .children(decl_primary)
5140                .first()
5141                .is_some_and(|&n| lang.literal(n) == Some(LitRef::Null));
5142            if !pinit_null {
5143                return None;
5144            }
5145            let r_init = lang.children(decl_r).first().copied()?;
5146            let ty = match lang.data(decl_r) {
5147                NodeData::VarDecl { ty, .. } => ty.clone(),
5148                _ => return None,
5149            };
5150            // 引用逃逸扫描
5151            let root = walk_root;
5152            // finally 整体被消费:卫语句内的 r/primary 引用均合法
5153            let r_allowed = [decl_r, r_init, ch[2], try_block];
5154            let t_allowed = [throw_stmt, assign_stmt, ch[1], ch[2]];
5155            let p_allowed = [decl_primary, assign_stmt, ch[2]];
5156            if refs_escape(lang, root, &r, &r_allowed)
5157                || refs_escape(lang, root, &primary, &p_allowed)
5158                || refs_escape(lang, root, &t, &t_allowed)
5159                || refs_escape(lang, root, &sup, &[ch[2]])
5160            {
5161                return None;
5162            }
5163            let init_copy = lang.copy_subtree(r_init);
5164            let res = lang.var_decl(&r, ty, Some(init_copy));
5165            let new_try = lang.try_(vec![res], try_block, Vec::new(), None);
5166            return Some(Edit::Splice {
5167                node: parent,
5168                index: idx - 2,
5169                remove: 3,
5170                insert: vec![new_try],
5171            });
5172        }
5173        None
5174    }
5175}
5176
5177// ---------------------------------------------------------------------------
5178// 字符串 switch 还原(string_switch_recover):javac v7 索引二级 switch
5179// 形态(ddc 直出;javac 字节码本相——hashCode 定位 + equals 守卫 + 索引
5180// switch 分发):
5181//   int v7 = 0;
5182//   switch (s.hashCode()) {
5183//       case H_i: {
5184//           boolean e = s.equals("L_i");
5185//           if (!e) { break; } else {
5186//               v7 = K_i;
5187//               switch (v7) { case K_1: BODY_1; ... default: BODY_DEF; }
5188//           }
5189//       } ...
5190//   }
5191//   v7 = -1;
5192//   switch (v7) { case K_1: BODY_1; ... default: BODY_DEF; }   // 无匹配路径
5193//   → switch (s) { case "L_1": BODY_1; ... default: BODY_DEF; }
5194//
5195// 守卫:选择器与 equals 接收方同为纯 VarRef(一次求值语义);
5196// 各索引 switch(含尾部)签名全同;每个 BODY 以终止语句收尾(无落穿,
5197// 标签序无关);v/e 引用全部落在被消费子树内;H_i ≡ "L_i".hashCode()。
5198// 哈希碰撞(同 case 双守卫)形态更复杂 → 整体不还原(负控锁定)。
5199// ---------------------------------------------------------------------------
5200
5201pub struct StringSwitchRecover;
5202
5203fn subtree_sig(lang: &JavaAst, n: JavaId, out: &mut String) {
5204    out.push_str(&format!("{:?} ", lang.data(n)));
5205    for &c in lang.children(n) {
5206        subtree_sig(lang, c, out);
5207    }
5208}
5209
5210/// 从 v7 索引 switch 提取 case K → 语句、default → 语句。
5211fn ssw_bodies(lang: &JavaAst, sw: JavaId) -> Option<Vec<(Option<i64>, Vec<JavaId>)>> {
5212    let ch = lang.children(sw).to_vec();
5213    let mut out = Vec::new();
5214    for &c in &ch[1..] {
5215        let NodeData::Case { labels, is_default, .. } = lang.data(c) else {
5216            return None;
5217        };
5218        let is_default = *is_default;
5219        let cc = lang.children(c);
5220        let label = if is_default {
5221            None
5222        } else {
5223            if *labels != 1 {
5224                return None; // 多标签 → 复杂形态,不还原
5225            }
5226            let lab = cc[0];
5227            let lit = lang.literal(lab)?;
5228            let v = match lit {
5229                LitRef::Int(v) | LitRef::Long(v) => v,
5230                _ => return None, // 非整型标签(字符串 case 是还原产物/负控)
5231            };
5232            Some(v)
5233        };
5234        out.push((label, cc[*labels as usize..].to_vec()));
5235    }
5236    Some(out)
5237}
5238
5239fn ssw_body_terminal(lang: &JavaAst, stmts: &[JavaId]) -> bool {
5240    let Some(&last) = stmts.last() else {
5241        return false; // 空体无终止
5242    };
5243    let last = if lang.kind(last) == NodeKind::Block {
5244        match lang.children(last).last() {
5245            Some(&l) => l,
5246            None => return false,
5247        }
5248    } else {
5249        last
5250    };
5251    matches!(
5252        lang.kind(last),
5253        NodeKind::Return | NodeKind::Break | NodeKind::Throw | NodeKind::Continue
5254    )
5255}
5256
5257impl Rule<JavaAst> for StringSwitchRecover {
5258    fn name(&self) -> &'static str {
5259        "string_switch_recover"
5260    }
5261    fn kinds(&self) -> &'static [NodeKind] {
5262        &[NodeKind::Switch]
5263    }
5264    fn structural(&self) -> bool {
5265        true
5266    }
5267    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
5268        let (parent, idx, ctx_root) = (ctx.parent(id)?, ctx.index(id)?, ctx.root());
5269        let lang = ctx.lang;
5270        if lang.kind(id) != NodeKind::Switch {
5271            return None;
5272        }
5273        let ch = lang.children(id).to_vec();
5274        if ch.len() < 2 {
5275            return None;
5276        }
5277        // 选择器 x.hashCode()
5278        let x = match lang.data(ch[0]) {
5279            NodeData::Call => {
5280                let cc = lang.children(ch[0]);
5281                if cc.len() != 1 {
5282                    return None;
5283                }
5284                match lang.data(cc[0]) {
5285                    NodeData::Member { name } if lang.sn(*name) == "hashCode" => {
5286                        let recv = *lang.children(cc[0]).first()?;
5287                        if lang.kind(recv) != NodeKind::VarRef {
5288                            return None;
5289                        }
5290                        lang.var_name(recv)?.to_string()
5291                    }
5292                    _ => return None,
5293                }
5294            }
5295            _ => {
5296                return None;
5297            }
5298        };
5299        // 各 hash case:单整型标签 + [decl_e?, if(!guard) break else {v=K, switch(v)}]
5300        struct CaseInfo {
5301            lit: String,
5302            k: i64,
5303            inner_switch: JavaId,
5304        }
5305        let mut infos: Vec<CaseInfo> = Vec::new();
5306        let mut v_name: Option<String> = None;
5307        let mut v_nodes: Vec<JavaId> = Vec::new(); // v 的全部合法出现(供逃逸扫描)
5308        let mut guard_nodes: Vec<JavaId> = Vec::new();
5309        for &c in &ch[1..] {
5310            let NodeData::Case { labels, is_default, .. } = lang.data(c) else {
5311                return None;
5312            };
5313            let is_default = *is_default;
5314            if is_default {
5315                return None; // hash switch 不应有 default(无匹配由尾部处理)
5316            }
5317            if *labels != 1 {
5318                return None;
5319            }
5320            let cc = lang.children(c);
5321            let h = match lang.literal(cc[0])? {
5322                LitRef::Int(v) | LitRef::Long(v) => v as i32,
5323                _ => return None,
5324            };
5325            let body = cc[1..].to_vec();
5326            // 单 Block 解包
5327            let body = if body.len() == 1 && lang.kind(body[0]) == NodeKind::Block {
5328                lang.children(body[0]).to_vec()
5329            } else {
5330                body
5331            };
5332            let mut rest = body;
5333            // 可选物化 boolean
5334            let mut _e_decl: Option<(String, JavaId)> = None;
5335            if let Some(&first) = rest.first() {
5336                if let NodeData::VarDecl { name, .. } = lang.data(first) {
5337                    if let Some(&init) = lang.children(first).first() {
5338                        if is_x_equals(lang, init, &x) {
5339                            _e_decl = Some((name.clone(), first));
5340                            rest = rest[1..].to_vec();
5341                        }
5342                    }
5343                }
5344            }
5345            if rest.len() != 1 || lang.kind(rest[0]) != NodeKind::If {
5346                return None;
5347            }
5348            let ifn = rest[0];
5349            let ic = lang.children(ifn).to_vec();
5350            if ic.len() != 3 {
5351                return None;
5352            }
5353            // cond = !guard(guard = e 或 x.equals(L))
5354            let NodeData::Unary { op: UnOp::Not } = lang.data(ic[0]) else {
5355                return None;
5356            };
5357            let guard = *lang.children(ic[0]).first()?;
5358            let lit = match lang.data(guard) {
5359                NodeData::VarRef { name } => {
5360                    // 物化 boolean:其唯一定义须是 x.equals("L")
5361                    let Some((en, decl)) = &_e_decl else {
5362                        return None;
5363                    };
5364                    if lang.sn(*name) != en {
5365                        return None;
5366                    }
5367                    let init = *lang.children(*decl).first()?;
5368                    equals_lit(lang, init, &x)?
5369                }
5370                NodeData::Call => equals_lit(lang, guard, &x)?,
5371                _ => return None,
5372            };
5373            guard_nodes.push(guard);
5374            if let Some((_, decl)) = &_e_decl {
5375                guard_nodes.push(*decl);
5376            }
5377            // then = break(无标签)
5378            let then_ok = twr_stmts(lang, ic[1]).len() == 1
5379                && matches!(lang.data(twr_stmts(lang, ic[1])[0]),
5380                    NodeData::Break { label: None });
5381            if !then_ok {
5382                return None;
5383            }
5384            // els = [v = K, switch(v)] 或 [switch(K)]
5385            let els = twr_stmts(lang, ic[2]);
5386            let (k, inner_switch, assign_node) = match els.len() {
5387                2 => {
5388                    let assign_expr = unwrap_expr_stmt(lang, els[0]);
5389                    let NodeData::Assign { op: None } = lang.data(assign_expr) else {
5390                        return None;
5391                    };
5392                    let a_ch = lang.children(assign_expr);
5393                    let vn = match lang.data(a_ch[0]) {
5394                        NodeData::VarRef { name } => lang.sn(*name).to_string(),
5395                        _ => return None,
5396                    };
5397                    let k = match lang.literal(a_ch[1])? {
5398                        LitRef::Int(v) | LitRef::Long(v) => v,
5399                        _ => return None,
5400                    };
5401                    if lang.kind(els[1]) != NodeKind::Switch {
5402                        return None;
5403                    }
5404                    // switch 选择器须为 v
5405                    let sel = *lang.children(els[1]).first()?;
5406                    if lang.kind(sel) != NodeKind::VarRef
5407                        || lang.var_name(sel) != Some(vn.as_str())
5408                    {
5409                        return None;
5410                    }
5411                    match &v_name {
5412                        Some(v) if *v != vn => return None,
5413                        _ => v_name = Some(vn),
5414                    }
5415                    (k, els[1], Some(els[0]))
5416                }
5417                1 => {
5418                    if lang.kind(els[0]) != NodeKind::Switch {
5419                        return None;
5420                    }
5421                    // 常量折叠后的形态:switch(K)
5422                    let sel = *lang.children(els[0]).first()?;
5423                    let k = match lang.literal(sel)? {
5424                        LitRef::Int(v) | LitRef::Long(v) => v,
5425                        _ => return None,
5426                    };
5427                    (k, els[0], None)
5428                }
5429                _ => return None,
5430            };
5431            if let Some(a) = assign_node {
5432                v_nodes.push(a);
5433            }
5434            v_nodes.push(*lang.children(inner_switch).first()?);
5435            // H ≡ hash(L)
5436            let hlit: i32 = lit
5437                .chars()
5438                .fold(0i32, |acc, ch| acc.wrapping_mul(31).wrapping_add(ch as i32));
5439            if hlit != h {
5440                return None;
5441            }
5442            let _ = assign_node;
5443            infos.push(CaseInfo {
5444                lit,
5445                k,
5446                inner_switch,
5447            });
5448        }
5449        if infos.is_empty() {
5450            return None;
5451        }
5452        // 父块 + 尾部
5453        if lang.kind(parent) != NodeKind::Block {
5454            return None;
5455        }
5456        let pch = lang.children(parent).to_vec();
5457        let mut k = idx + 1;
5458        // 可选 v = -1
5459        if k < pch.len() {
5460            let texpr = unwrap_expr_stmt(lang, pch[k]);
5461            if let NodeData::Assign { op: None } = lang.data(texpr) {
5462                let a_ch = lang.children(texpr);
5463                if a_ch.len() == 2
5464                    && lang.kind(a_ch[0]) == NodeKind::VarRef
5465                    && lang.var_name(a_ch[0]) == v_name.as_deref()
5466                    && lang.literal(a_ch[1]) == Some(LitRef::Int(-1))
5467                {
5468                    v_nodes.push(pch[k]);
5469                    k += 1;
5470                }
5471            }
5472        }
5473        if k >= pch.len() || lang.kind(pch[k]) != NodeKind::Switch {
5474            return None;
5475        }
5476        let trailing = pch[k];
5477        // 尾部选择器:VarRef v 或常量
5478        let tsel = *lang.children(trailing).first()?;
5479        match lang.data(tsel) {
5480            NodeData::VarRef { name } if Some(lang.sn(*name)) == v_name.as_deref() => {
5481                v_nodes.push(tsel);
5482            }
5483            NodeData::Literal(Lit::Int(_)) | NodeData::Literal(Lit::Long(_)) => {}
5484            _ => {
5485                lang.literal(tsel)?;
5486            }
5487        }
5488        // 签名一致性:所有 inner switch + trailing 的**case 体**全同
5489        //(选择器除外——尾部的 v 常被赋值传播折叠成字面量)
5490        fn switch_cases_sig(lang: &JavaAst, sw: JavaId, out: &mut String) {
5491            for &c in &lang.children(sw)[1..] {
5492                subtree_sig(lang, c, out);
5493            }
5494        }
5495        let mut sig0 = String::new();
5496        switch_cases_sig(lang, infos[0].inner_switch, &mut sig0);
5497        for info in &infos[1..] {
5498            let mut sig = String::new();
5499            switch_cases_sig(lang, info.inner_switch, &mut sig);
5500            if sig != sig0 {
5501                return None;
5502            }
5503        }
5504        {
5505            let mut sig = String::new();
5506            switch_cases_sig(lang, trailing, &mut sig);
5507            if sig != sig0 {
5508                return None;
5509            }
5510        }
5511        // 提取 body(用第一个 inner switch)
5512        let bodies = ssw_bodies(lang, infos[0].inner_switch)?;
5513        let mut body_map: std::collections::HashMap<i64, Vec<JavaId>> =
5514            std::collections::HashMap::new();
5515        let mut default_body: Option<Vec<JavaId>> = None;
5516        for (lab, stmts) in &bodies {
5517            match lab {
5518                Some(v) => {
5519                    body_map.insert(*v, stmts.clone());
5520                }
5521                None => default_body = Some(stmts.clone()),
5522            }
5523        }
5524        let Some(default_body) = default_body else {
5525            return None;
5526        };
5527        // 终止性
5528        for stmts in body_map.values().chain(std::iter::once(&default_body)) {
5529            if !ssw_body_terminal(lang, stmts) {
5530                return None;
5531            }
5532        }
5533        // v/e 逃逸扫描(v 的声明本身合法——索引变量;其引用全在消费子树内)
5534        let root = ctx_root;
5535        if let Some(vn) = &v_name {
5536            let mut allowed: Vec<JavaId> = v_nodes.clone();
5537            allowed.push(trailing);
5538            if idx >= 1 {
5539                let cand = pch[idx - 1];
5540                if let NodeData::VarDecl { name, .. } = lang.data(cand) {
5541                    if name == vn {
5542                        allowed.push(cand);
5543                        // 声明位置紧邻(ddc 形态);隔开的声明仍拒绝
5544                        if idx != 1 {
5545                            allowed.pop();
5546                        }
5547                    }
5548                }
5549            }
5550            if refs_escape(lang, root, vn, &allowed) {
5551                return None;
5552            }
5553        }
5554        // 构建 switch (x) { case "L": BODY_K; ... default: BODY_DEF }
5555        let mut new_cases: Vec<JavaId> = Vec::new();
5556        for info in &infos {
5557            let Some(body) = body_map.get(&info.k) else {
5558                return None;
5559            };
5560            let lab = lang.lit(Lit::Str(info.lit.clone()));
5561            let case = lang.case_(vec![lab], false, false, body.clone());
5562            new_cases.push(case);
5563        }
5564        let def_case = lang.case_(Vec::new(), true, false, default_body);
5565        new_cases.push(def_case);
5566        let sel = lang.var(&x);
5567        let new_switch = lang.switch_(sel, new_cases);
5568        // 消费 [hash switch, (v=-1)?, trailing switch]
5569        let remove = k - idx + 1;
5570        Some(Edit::Splice {
5571            node: parent,
5572            index: idx,
5573            remove,
5574            insert: vec![new_switch],
5575        })
5576    }
5577}
5578
5579/// Call{x.equals("L")} → L
5580fn equals_lit(lang: &JavaAst, e: JavaId, x: &str) -> Option<String> {
5581    if lang.kind(e) != NodeKind::Call {
5582        return None;
5583    }
5584    let cc = lang.children(e);
5585    if cc.len() != 2 {
5586        return None;
5587    }
5588    match lang.data(cc[0]) {
5589        NodeData::Member { name } if lang.sn(*name) == "equals" => {
5590            let recv = *lang.children(cc[0]).first()?;
5591            if lang.kind(recv) != NodeKind::VarRef || lang.var_name(recv) != Some(x) {
5592                return None;
5593            }
5594            match lang.literal(cc[1])? {
5595                LitRef::Str(s) => Some(s.to_string()),
5596                _ => None,
5597            }
5598        }
5599        _ => None,
5600    }
5601}
5602
5603fn is_x_equals(lang: &JavaAst, e: JavaId, x: &str) -> bool {
5604    lang.kind(e) == NodeKind::Call && equals_lit(lang, e, x).is_some()
5605}
5606
5607// ---------------------------------------------------------------------------
5608// 门面
5609// ---------------------------------------------------------------------------
5610
5611
5612/// Java 默认规则集(引擎通用规则 + Java 特有规则;保守集,不含 DCE 类)。
5613pub fn default_java_rules() -> Vec<Box<dyn Rule<JavaAst>>> {
5614    let mut rules: Vec<Box<dyn Rule<JavaAst>>> = cure_engine::rules::default_rules();
5615    rules.push(Box::new(CastSimplify));
5616    rules.push(Box::new(SelfCompare));
5617    rules.push(Box::new(StringBuilderFold));
5618    rules.push(Box::new(BoxUnboxChain));
5619    rules.push(Box::new(IteratorToForEach));
5620    rules.push(Box::new(NewStringFold));
5621    rules.push(Box::new(NewStringCharArrayFold));
5622    rules.push(Box::new(EmptyFinallyStrip));
5623    rules.push(Box::new(TryUnwrapNoCatch));
5624    rules.push(Box::new(TryUnwrapRethrow));
5625    rules.push(Box::new(StaticExec));
5626    rules.push(Box::new(LoopHeadBreak));
5627    rules.push(Box::new(WhileIteratorToForEach));
5628    rules.push(Box::new(ConcatValueOfDrop));
5629    rules.push(Box::new(StringBuilderStatements));
5630    rules.push(Box::new(TrailingContinueJava));
5631    rules.push(Box::new(XorNoise));
5632    rules.push(Box::new(StrLenFold));
5633    rules.push(Box::new(LiteralEval));
5634    rules.push(Box::new(Base64NewStringFold));
5635    rules.push(Box::new(UrlDecodeFold));
5636    rules.push(Box::new(CffRecover));
5637    rules.push(Box::new(StaticArrayIndexFold));
5638    rules.push(Box::new(TrailingAssignReturn));
5639    rules.push(Box::new(TrailingIfContinue));
5640    rules.push(Box::new(ConstFieldPropagate));
5641    rules.push(Box::new(NoopPrivateCall));
5642    rules.push(Box::new(ConstMethodInline));
5643    rules.push(Box::new(TwrRecover));
5644    rules.push(Box::new(StringSwitchRecover));
5645    rules
5646}
5647
5648/// 全量规则(含 DCE 类选配项:不可达语句删除等)。
5649pub fn all_java_rules() -> Vec<Box<dyn Rule<JavaAst>>> {
5650    let mut rules = default_java_rules();
5651    let mut engine_all = cure_engine::rules::all_rules();
5652    rules.append(&mut engine_all);
5653    rules
5654}
5655
5656/// 对方法体(或任意语句 Block)运行 Java 规则集直到 fixed point。
5657pub fn simplify(ast: &mut JavaAst, root: JavaId, cfg: &Config) -> Report {
5658    let rules = default_java_rules();
5659    cure_engine::simplify(ast, root, &rules, cfg)
5660}
5661
5662/// 整个编译单元:逐个方法体/初始化块/字段初始化器优化,签名不动。
5663pub fn simplify_unit(ast: &mut JavaAst, unit: &mut CompilationUnit, cfg: &Config) -> Report {
5664    let mut total = Report::default();
5665    // 收敛外循环:引擎单次调用跑至"本轮无应用"即停,但末轮被丢弃的结构
5666    // 提案(目标父在搬移后过期,需新鲜 walk 复活)会漏到下一次调用——
5667    // 外层重跑到不动点(上限防乒乓;真实语料 1-2 轮清零,javaparser 6 个
5668    // 幂等失败文件抓获:const_method_inline/if_else_empty 二轮仍有编辑)
5669    for _cycle in 0..6 {
5670        let mut round = Report::default();
5671        collect_unit_consts(ast, unit);
5672        // no-op 调用清理表(--dead-code 门控):off 时清空——规则查询
5673        // 空表自然不触发
5674        if cfg.remove_dead_methods {
5675            collect_noop_private_methods(ast, unit);
5676        } else {
5677            ast.noop_private_methods.clear();
5678        }
5679        for ty in &mut unit.types {
5680            simplify_type(ast, ty, cfg, &mut round);
5681        }
5682        if cfg.remove_dead_methods {
5683            round.edits += remove_dead_private_fields(ast, unit);
5684            round.edits += remove_dead_private_methods(ast, unit);
5685            round.edits += remove_unused_imports(ast, unit);
5686        }
5687        total.edits += round.edits;
5688        total.iterations += round.iterations;
5689        total.discarded += round.discarded;
5690        for (k, v) in round.by_rule {
5691            *total.by_rule.entry(k).or_insert(0) += v;
5692        }
5693        // 注:确认轮不可省——mega 实测驳回过"零丢弃即不动点"短路:跨轮
5694        // 编辑的主来源是 collect 表过期(内联/折叠后新常量/新单 return 方法
5695        // 涌现,重建表后才可见),非引擎丢弃提案的复活。RSS +2-3MB 是
5696        // 收敛正确性的价格。
5697        if round.edits == 0 {
5698            break;
5699        }
5700    }
5701    total
5702}
5703
5704// ---------------------------------------------------------------------------
5705/// 匿名类体(NodeData::New{anon_raw} 原文)里的标识符全集 + 字符串字面量
5706/// 简单名全集(反射按名查找:MethodHandles.findStatic(X, "putByte", …))。
5707/// 死码分析的引用扫描对二者均不可见——IteratorWildcard 抓获:匿名体内
5708/// 调用的 sideCast/adaptSelf 被删(cannot find symbol)、DomainCombiner
5709/// import 被删、findStatic("putByte") 目标被删(ExceptionInInitializerError)。
5710fn collect_anon_and_string_names(
5711    ast: &JavaAst,
5712    unit: &CompilationUnit,
5713) -> std::collections::HashSet<String> {
5714    let mut out: std::collections::HashSet<String> = Default::default();
5715    let mut bodies: Vec<JavaId> = Vec::new();
5716    for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut bodies));
5717    for root in bodies {
5718        let mut stack = vec![root];
5719        while let Some(n) = stack.pop() {
5720            match ast.data(n) {
5721                NodeData::New { anon_raw: Some(raw), .. } => {
5722                    for id in raw.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
5723                        if !id.is_empty() {
5724                            out.insert(id.to_string());
5725                        }
5726                    }
5727                }
5728                NodeData::Literal(Lit::Str(text)) => {
5729                    for id in text.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
5730                        if !id.is_empty() {
5731                            out.insert(id.to_string());
5732                        }
5733                    }
5734                }
5735                _ => {}
5736            }
5737            for &c in ast.children(n) {
5738                stack.push(c);
5739            }
5740        }
5741    }
5742    out
5743}
5744
5745// 死私有方法删除(opt-in,Config::remove_dead_methods):
5746// const_method_inline / 解密器还原后残留的 helper 清理。
5747// 安全域:
5748//   - 仅 private(private 不可能被外部调用或被子类覆盖);
5749//   - 全单元零引用:Call 的 callee 名(Member 名/裸名)与 MethodRef 名,
5750//     名字匹配不判签名——匹配只会导致"保留",方向保守;
5751//     (引用扫描是方法命名空间的完整覆盖:字段名与方法名解析域不相交
5752//     (bd.java 字段 b 与方法 b 共存);方法重载撞名但零调用 = 同名
5753//     任何方法都未被调用,private 者可删——旧字段计数守卫曾误留
5754//     bd 的 8 个空方法));
5755//   - 修饰符串含 @(注解方法,可能是框架入口)→ 保留;
5756//   - 构造器不删(单例模式 private ctor 是活的)。
5757// 反射调用无法静态排除 → opt-in 而非默认。
5758// 传递性死代码:迭代到不动点(删一层后重收集引用)。
5759// ---------------------------------------------------------------------------
5760
5761/// **死码分析统一超集扫描**:单元内对「引用扫描不可见域」的全量兜底。
5762/// 覆盖(第 9 轮 dead-code 全审 761 处悬空的 6 类根因):
5763///   - NodeData::Raw 文本(语句级注解 → Raw:guava TypeToken 34 个
5764///     悬空方法主因)
5765///   - New{anon_raw} 匿名类体(局部类调用:grpc 15 个方法)
5766///   - 成员 mods 注解实参(@Method("name") 按名反射 / @MethodSource)
5767///   - 枚举常量原文(guava CaseFormat 等调用在枚举体)
5768///   - ForEach 元素类型(import 悬空 383 例)
5769///   - NewArray 数组类型(import 悬空)
5770///   - 方法级 ty_params 泛型界(jspecify @Nullable 90 例)
5771///   - 字符串字面量(反射按名查找)
5772/// 返回:名字出现集(保守超集——名字出现在任何不可见域即视作引用)
5773fn collect_all_blind_refs(
5774    ast: &JavaAst,
5775    unit: &CompilationUnit,
5776) -> std::collections::HashSet<String> {
5777    let mut out: std::collections::HashSet<String> = Default::default();
5778    let split_into = |text: &str, out: &mut std::collections::HashSet<String>| {
5779        for id in text.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
5780            if !id.is_empty() {
5781                out.insert(id.to_string());
5782            }
5783        }
5784    };
5785    // 1) AST 节点层:Raw / anon_raw / 字符串字面量 / ForEach ty / NewArray ty
5786    let mut bodies: Vec<JavaId> = Vec::new();
5787    for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut bodies));
5788    for root in bodies {
5789        let mut stack = vec![root];
5790        while let Some(n) = stack.pop() {
5791            match ast.data(n) {
5792                NodeData::Raw { text } => split_into(text, &mut out),
5793                NodeData::New { anon_raw: Some(raw), .. } => split_into(raw, &mut out),
5794                NodeData::Literal(Lit::Str(text)) => split_into(text, &mut out),
5795                // ForEach 元素类型(类型名在节点数据不在孩子)
5796                NodeData::ForEach { ty, .. } => {
5797                    collect_ty_idents(ty, &mut out);
5798                }
5799                // NewArray 数组创建类型
5800                NodeData::NewArray { ty, .. } => {
5801                    collect_ty_idents(ty, &mut out);
5802                }
5803                // Lambda 显式参数类型((Map.Entry<K, V> entry) -> ——params_raw
5804                // 是载荷字符串孩子不走):auto MoreStreams 抓获——
5805                // `import java.util.Map` 曾因 lambda 参数类型不可见被误删
5806                NodeData::Lambda { params_raw } => {
5807                    split_into(params_raw, &mut out);
5808                }
5809                _ => {}
5810            }
5811            for &c in ast.children(n) {
5812                stack.push(c);
5813            }
5814        }
5815    }
5816    // 1.6) 顶层 raw 区(unit.raws——破损输入的保真残段):其文本中的
5817    // 类型/名字引用对一切句法扫描不可见(BuilderSimpleOnRecord 抓获:
5818    // `<T> {…List<T> l…}` 残段内的 List 曾不可见 → import 误删悬空)
5819    for r in &unit.raws {
5820        split_into(r, &mut out);
5821    }
5822    // 2) 结构层:成员 mods 注解实参 / 枚举常量原文 / 方法级 ty_params /
5823    //    record 组件头
5824    for_each_type(unit, &mut |ty: &TypeDecl| {
5825        collect_mods_idents_all(&ty.mods, &mut out);
5826        for e in &ty.enum_constants {
5827            split_into(e, &mut out);
5828        }
5829        split_into(&ty.ty_params, &mut out);
5830        split_into(&ty.header, &mut out);
5831        for m in &ty.members {
5832            match m {
5833                Member::Method { mods, name, ty_params, params, .. }
5834                | Member::Constructor { mods, name, ty_params, params, .. } => {
5835                    collect_mods_idents_all(mods, &mut out);
5836                    split_into(ty_params, &mut out);
5837                    for p in params {
5838                        collect_mods_idents_all(&p.mods, &mut out);
5839                        collect_ty_idents(&p.ty, &mut out);
5840                    }
5841                    // 裸 @MethodSource(无实参)——JUnit5 约定:测试方法自身
5842                    // 名字即工厂方法名(junit5 BeforeAndAfterSuiteTests /
5843                    // commons-io IOUtilsTest 抓获:工厂误删运行时
5844                    // MethodSource 解析失败)
5845                    if has_bare_methodsource(mods) {
5846                        out.insert(name.clone());
5847                    }
5848                }
5849                Member::Field { mods, declarators, .. } => {
5850                    collect_mods_idents_all(mods, &mut out);
5851                    // 注意:字段**声明名**不进 blind 集(自己删自己永真)
5852                    let _ = declarators;
5853                }
5854                Member::Type(t) => {
5855                    let _ = t; // for_each_type 已递归
5856                }
5857                // @interface 成员整段 Raw(annotation 成员无专用节点):
5858                // 其中的类型/常量/方法引用对一切句法扫描不可见
5859                //(junit5 Timeout 抓获:`TimeUnit unit() default TimeUnit
5860                // .SECONDS;` 曾使 import java.util.concurrent.TimeUnit
5861                // 误删悬空 28 文件;AnnoCaller 复现:Raw 体内调用的
5862                // 私有方法/字段曾误删)
5863                Member::Raw(text) => {
5864                    split_into(text, &mut out);
5865                }
5866                _ => {}
5867            }
5868        }
5869    });
5870    out
5871}
5872
5873/// mods 全文本切分(含注解实参——@Method(METHOD_NAME) 常量引用 /
5874/// @MethodSource("name") 按名反射——dubbo MethodConfigTest 34 个
5875/// 悬空字段主因)。
5876fn collect_mods_idents_all(mods: &str, out: &mut std::collections::HashSet<String>) {
5877    for id in mods.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
5878        if !id.is_empty() {
5879            out.insert(id.to_string());
5880        }
5881    }
5882}
5883
5884/// mods 中是否出现**裸** `@MethodSource`(注解名后不接 `(`——简单名或
5885/// 全限定名均可:`@MethodSource` / `@org.junit.jupiter.params.provider
5886/// .MethodSource`)。
5887/// JUnit5 约定:无实参的 @MethodSource 按测试方法自身名字解析工厂——
5888/// 该名字必须按「已引用」处理(具名实参 `@MethodSource("name")` 的
5889/// 名字已由 mods 切分覆盖)。
5890fn has_bare_methodsource(mods: &str) -> bool {
5891    let hay = mods.as_bytes();
5892    const NEEDLE: &[u8] = b"MethodSource";
5893    let mut i = 0usize;
5894    while let Some(p) = mods[i..].find("MethodSource") {
5895        let at = i + p;
5896        let end = at + NEEDLE.len();
5897        // 前驱须为 @ 或 .(排除 MyMethodSource 类内嵌匹配);
5898        // 后继不得是标识符续字符(排除后缀内嵌)
5899        let prev_ok = at == 0
5900            || !(hay[at - 1].is_ascii_alphanumeric()
5901                || hay[at - 1] == b'_'
5902                || hay[at - 1] == b'$');
5903        let next_ident =
5904            end < hay.len() && (hay[end].is_ascii_alphanumeric() || hay[end] == b'_' || hay[end] == b'$');
5905        if prev_ok && !next_ident {
5906            let mut j = end;
5907            while j < hay.len() && hay[j].is_ascii_whitespace() {
5908                j += 1;
5909            }
5910            if j >= hay.len() || hay[j] != b'(' {
5911                return true;
5912            }
5913        }
5914        i = end;
5915    }
5916    false
5917}
5918
5919/// JVM/库按名反射调用的回调方法(序列化机制等)——零句法调用者是
5920/// 正常形态,删除即语义破坏(IteratorWildcard 抓获:writeObject 删除
5921/// 失去串行化同步与权限预检)。
5922const REFLECTIVE_MAGIC_METHODS: &[&str] = &[
5923    "writeObject",
5924    "readObject",
5925    "readObjectNoData",
5926    "writeReplace",
5927    "readResolve",
5928    "validateObject",
5929];
5930
5931fn remove_dead_private_methods(ast: &JavaAst, unit: &mut CompilationUnit) -> usize {
5932    use std::collections::HashSet;
5933    let mut removed = 0usize;
5934    // 匿名类体/字符串字面量中出现的名字(引用扫描不可见域)
5935    let anon_names = collect_anon_and_string_names(ast, unit);
5936    // **超集盲扫**:Raw 文本/枚举体/注解实参/局部类等对句法引用扫描
5937    // 不可见的域(第 9 轮全审 34 个悬空方法的 6 类根因——guava
5938    // TypeToken 的 @SuppressWarnings 局部声明、grpc 局部类体、
5939    // rocketmq record 变量名、guava 枚举常量体调用)
5940    let blind = collect_all_blind_refs(ast, unit);
5941    loop {
5942        // 1) 全单元引用(不可变借用阶段):(名字, 实参个数) 精确到元数
5943        //    (非 varargs 下元数=形参数是过载解析的硬约束——bd.java 的
5944        //    public b(String)/b(String,Class,Class[]) 与 private 空
5945        //    b(Hashtable,MessageDigest) 同名不同元数可分离);
5946        //    MethodRef(this::m)与同名 varargs 方法 → 元数不可判定,
5947        //    名字整体否决。
5948        let mut referenced: HashSet<(String, usize)> = HashSet::new();
5949        let mut veto: HashSet<String> = HashSet::new();
5950        let mut roots: Vec<JavaId> = Vec::new();
5951        for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut roots));
5952        for body in roots {
5953            collect_call_arity_refs(ast, body, &mut referenced, &mut veto);
5954        }
5955        for_each_type(unit, &mut |ty| {
5956            for m in &ty.members {
5957                if let Member::Method { name, params, .. } = m {
5958                    if params.iter().any(|p| p.varargs) {
5959                        veto.insert(name.clone());
5960                    }
5961                }
5962            }
5963        });
5964        // 2) 删除(可变借用阶段)
5965        let mut changed = false;
5966        for_each_type_mut(unit, &mut |ty: &mut TypeDecl| {
5967            let before = ty.members.len();
5968            ty.members.retain(|m| {
5969                if let Member::Method { mods, name, params, .. } = m {
5970                    let is_private = mods.split_whitespace().any(|w| w == "private");
5971                    let annotated = mods.contains('@');
5972                    let keep = !is_private
5973                        || annotated
5974                        // 自身 varargs:元数匹配不可靠 → 保留
5975                        || params.iter().any(|p| p.varargs)
5976                        || veto.contains(name)
5977                        // 序列化等机制按名反射调用(无句法调用者是正常
5978                        // 形态);名字出现在字符串字面量(findStatic/
5979                        // getMethod 按名查找)或匿名类体 → 保守保留
5980                        || REFLECTIVE_MAGIC_METHODS.contains(&name.as_str())
5981                        || anon_names.contains(name)
5982                        || blind.contains(name)
5983                        || referenced.contains(&(name.clone(), params.len()));
5984                    keep
5985                } else {
5986                    true
5987                }
5988            });
5989            if ty.members.len() != before {
5990                removed += before - ty.members.len();
5991                changed = true;
5992            }
5993        });
5994        if !changed {
5995            return removed;
5996        }
5997    }
5998}
5999
6000/// 剥离 callee/MethodRef 名里的前导类型实参(witness 保留把
6001/// `<String>pick` 整体并入 Member 名——第 11 轮攻击抓获:死码裸名
6002/// 精确匹配失明 → 私有方法误删输出不可编译)。名字以 `<` 开头必为
6003/// TA 前缀(标识符/`class`/`new` 等形态不可能以 `<` 开头)。
6004fn strip_ta_prefix(name: &str) -> &str {
6005    if !name.starts_with('<') {
6006        return name;
6007    }
6008    let b = name.as_bytes();
6009    let mut depth = 0i32;
6010    for (i, &c) in b.iter().enumerate() {
6011        match c {
6012            b'<' => depth += 1,
6013            b'>' => {
6014                depth -= 1;
6015                if depth == 0 {
6016                    return &name[i + 1..];
6017                }
6018            }
6019            _ => {}
6020        }
6021    }
6022    name // 不平衡(病态)——原样保守
6023}
6024
6025/// (名字, 实参元数) 引用 + MethodRef/未知元数名字否决。
6026fn collect_call_arity_refs(
6027    ast: &JavaAst,
6028    root: JavaId,
6029    out: &mut std::collections::HashSet<(String, usize)>,
6030    veto: &mut std::collections::HashSet<String>,
6031) {
6032    let mut stack = vec![root];
6033    while let Some(id) = stack.pop() {
6034        match ast.data(id) {
6035            NodeData::Call => {
6036                let ch = ast.children(id).to_vec();
6037                if let Some(&callee) = ch.first() {
6038                    let name = match ast.data(callee) {
6039                        NodeData::Member { name } | NodeData::VarRef { name } => {
6040                            ast.sn(*name).to_string()
6041                        }
6042                        _ => String::new(),
6043                    };
6044                    if !name.is_empty() {
6045                        out.insert((strip_ta_prefix(&name).to_string(), ch.len() - 1));
6046                    }
6047                }
6048            }
6049            NodeData::MethodRef { name } => {
6050                if let Some(short) = ast.sn(*name).rsplit("::").next() {
6051                    veto.insert(strip_ta_prefix(short).to_string());
6052                }
6053            }
6054            _ => {}
6055        }
6056        for &c in ast.children(id) {
6057            stack.push(c);
6058        }
6059    }
6060}
6061
6062/// 递归访问(含嵌套类型),只读。
6063fn for_each_type(unit: &CompilationUnit, f: &mut dyn FnMut(&TypeDecl)) {
6064    for ty in &unit.types {
6065        for_each_type_inner(ty, f);
6066    }
6067}
6068fn for_each_type_inner(ty: &TypeDecl, f: &mut dyn FnMut(&TypeDecl)) {
6069    f(ty);
6070    for m in &ty.members {
6071        if let Member::Type(t) = m {
6072            for_each_type_inner(t, f);
6073        }
6074    }
6075}
6076
6077/// 递归访问(含嵌套类型),可变。
6078fn for_each_type_mut(unit: &mut CompilationUnit, f: &mut dyn FnMut(&mut TypeDecl)) {
6079    for ty in &mut unit.types {
6080        for_each_type_mut_inner(ty, f);
6081    }
6082}
6083fn for_each_type_mut_inner(ty: &mut TypeDecl, f: &mut dyn FnMut(&mut TypeDecl)) {
6084    f(ty);
6085    for m in &mut ty.members {
6086        if let Member::Type(t) = m {
6087            for_each_type_mut_inner(t, f);
6088        }
6089    }
6090}
6091
6092/// 汇集一个类型所有可走子树的根(方法体/初始化块/字段初始化器)。
6093fn collect_member_roots(ty: &TypeDecl, out: &mut Vec<JavaId>) {
6094    for m in &ty.members {
6095        match m {
6096            Member::Method { body: Some(b), .. }
6097            | Member::Constructor { body: Some(b), .. } => out.push(*b),
6098            Member::Initializer { body, .. } => out.push(*body),
6099            Member::Field { declarators, .. } => {
6100                for d in declarators {
6101                    if let Some(init) = d.init {
6102                        out.push(init);
6103                    }
6104                }
6105            }
6106            Member::Type(t) => collect_member_roots(t, out),
6107            _ => {}
6108        }
6109    }
6110}
6111
6112/// 子树内收集所有"被调用"的名字:Call 的 callee(Member 名 / 裸 VarRef 名)
6113/// 与 MethodRef 名(`recv::name`)。名字匹配,不判签名。
6114// ---------------------------------------------------------------------------
6115// 空私有方法 no-op 调用清理 + 死私有字段删除(均 opt-in,--dead-code)
6116// 与 remove_dead_private_methods 同一风险域(反射无法静态排除)。
6117// ---------------------------------------------------------------------------
6118
6119/// 收集同名同元数方法候选(名字 → 元数 → 候选表)。
6120/// 资格(全单元递归):
6121///   - 修饰符串含 @(注解框架入口)→ 名字一票否决;
6122///   - varargs(可吸收任意元数调用)→ 名字一票否决;
6123///   - 候选按 (private && 空体) 标记 noop——调用点做类型适用性消解后
6124///     唯一适用且 noop 才删(bd.java:public c(Class, String) 与
6125///     private c(Hashtable, MessageDigest) 同元数,实参类型消解分离)。
6126fn collect_noop_private_methods(ast: &mut JavaAst, unit: &CompilationUnit) {
6127    use std::collections::{HashMap, HashSet};
6128    ast.noop_private_methods.clear();
6129    let mut by_name: HashMap<String, HashMap<usize, Vec<NoopCandidate>>> = HashMap::new();
6130    let mut veto: HashSet<String> = HashSet::new();
6131    for_each_type(unit, &mut |ty: &TypeDecl| {
6132        for m in &ty.members {
6133            if let Member::Method { mods, name, params, body, throws, .. } = m {
6134                if mods.contains('@') || params.iter().any(|p| p.varargs) {
6135                    veto.insert(name.clone());
6136                    continue;
6137                }
6138                let is_private = mods.split_whitespace().any(|w| w == "private");
6139                let empty = match body {
6140                    Some(b) => ast.kind(*b) == NodeKind::Block && ast.children(*b).is_empty(),
6141                    None => false,
6142                };
6143                // throws 非空:删除调用会使外层 catch 失去「异常可在 try
6144                // 体内抛出」的编译依据(InputAstRegressionManyAlternatives
6145                // 抓获:删 foo1() 后 catch(SQLException) 变编译错误)
6146                let no_throws = throws.is_empty();
6147                let cand = NoopCandidate {
6148                    params: params.iter().map(|p| p.ty.clone()).collect(),
6149                    noop: is_private && empty && no_throws,
6150                };
6151                by_name
6152                    .entry(name.clone())
6153                    .or_default()
6154                    .entry(params.len())
6155                    .or_default()
6156                    .push(cand);
6157            }
6158        }
6159    });
6160    // 超集盲扫否决(Raw 文本/枚举体/注解实参里的方法名引用——
6161    // no-op 删除后这些域的调用会悬空)
6162    let blind = collect_all_blind_refs(ast, unit);
6163    // MethodRef(this::m / A::m):绑定元数不可判定 → 名字否决
6164    //(否则 no-op 调用删除先行,引用悬空)
6165    let mut roots: Vec<JavaId> = Vec::new();
6166    for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut roots));
6167    for root in roots {
6168        let mut stack = vec![root];
6169        while let Some(n) = stack.pop() {
6170            if let NodeData::MethodRef { name } = ast.data(n) {
6171                if let Some(short) = ast.sn(*name).rsplit("::").next() {
6172                    veto.insert(strip_ta_prefix(short).to_string());
6173                }
6174            }
6175            for &c in ast.children(n) {
6176                stack.push(c);
6177            }
6178        }
6179    }
6180    for (name, arities) in by_name {
6181        if veto.contains(&name) || blind.contains(&name) {
6182            continue;
6183        }
6184        ast.noop_private_methods.insert(name, arities);
6185    }
6186}
6187
6188/// 未用单类型 import 清理(--dead-code 收尾):简单名在全单元标识符
6189/// 集合(AST 名字/类型 + 结构字符串含泛型实参)中零出现 → 删。
6190/// on-demand(.*)/ static import 保守保留;误删只会在标识符收集
6191/// 漏形式时发生——方向上宁可多保留。
6192fn remove_unused_imports(ast: &JavaAst, unit: &mut CompilationUnit) -> usize {
6193    use std::collections::HashSet;
6194    let mut idents: HashSet<String> = HashSet::new();
6195    // 1) AST 载荷标识符(名字 + 类型 + Raw 原文)
6196    let mut roots: Vec<JavaId> = Vec::new();
6197    for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut roots));
6198    for root in roots {
6199        let mut stack = vec![root];
6200        while let Some(n) = stack.pop() {
6201            match ast.data(n) {
6202                NodeData::VarDecl { name, ty, .. } => {
6203                    idents.insert(name.clone());
6204                    collect_ty_idents(ty, &mut idents);
6205                }
6206                NodeData::VarRef { name } => {
6207                    // 表达式里的 Foo.class 是单个 VarRef(名字="Foo.class"
6208                    // 整串)——按分隔符切出类型名(v.java List.class 抓获:
6209                    // import 误删)
6210                    let n = ast.sn(*name);
6211                    for id in n.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6212                        if !id.is_empty() {
6213                            idents.insert(id.to_string());
6214                        }
6215                    }
6216                }
6217                NodeData::Member { name } => {
6218                    idents.insert(ast.sn(*name).to_string());
6219                }
6220                NodeData::MethodRef { name } => {
6221                    let n = ast.sn(*name);
6222                    for id in n.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6223                        if !id.is_empty() {
6224                            idents.insert(id.to_string());
6225                        }
6226                    }
6227                }
6228                NodeData::Label { name } => {
6229                    idents.insert(name.clone());
6230                }
6231                NodeData::New { ty, anon_raw, .. } => {
6232                    collect_ty_idents(ty, &mut idents);
6233                    // 匿名类体原文:其中的类型/名字引用对 AST 扫描不可见
6234                    //(IteratorWildcard 抓获:DomainCombiner import 误删)
6235                    if let Some(raw) = anon_raw {
6236                        for id in
6237                            raw.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$')
6238                        {
6239                            if !id.is_empty() {
6240                                idents.insert(id.to_string());
6241                            }
6242                        }
6243                    }
6244                }
6245                NodeData::Cast { ty } => {
6246                    collect_ty_idents(ty, &mut idents);
6247                }
6248                NodeData::InstanceOf { ty, .. } => {
6249                    collect_ty_idents(ty, &mut idents);
6250                }
6251                NodeData::Catch { ty_raw, .. } => {
6252                    // catch (Type name)——异常类型原文
6253                    for id in
6254                        ty_raw.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$')
6255                    {
6256                        if !id.is_empty() {
6257                            idents.insert(id.to_string());
6258                        }
6259                    }
6260                }
6261                NodeData::Raw { text } => {
6262                    for id in text.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6263                        if !id.is_empty() {
6264                            idents.insert(id.to_string());
6265                        }
6266                    }
6267                }
6268                _ => {}
6269            }
6270            for &c in ast.children(n) {
6271                stack.push(c);
6272            }
6273        }
6274    }
6275    // 1.5) 超集盲扫(Raw/枚举体/注解实参/ForEach ty/NewArray ty/
6276    //      方法级 ty_params——657 个悬空 import 的根因。idents 是
6277    //      「使用的名字」超集方向,直接并入)
6278    for name in collect_all_blind_refs(ast, unit) {
6279        idents.insert(name);
6280    }
6281    // 1.6) 顶层 raw 区(unit.raws):残段文本中的类型引用(同上——
6282    // 曾不可见使 List 误删)
6283    for r in &unit.raws {
6284        for id in r.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6285            if !id.is_empty() {
6286                idents.insert(id.to_string());
6287            }
6288        }
6289    }
6290    // 2) 结构字符串(mods/注解原文、泛型参数、extends/implements/throws、
6291    //    方法/类型名——字段/参数/返回类型)
6292    for_each_type(unit, &mut |ty| {
6293        collect_mods_idents(&ty.mods, &mut idents);
6294        idents.insert(ty.name.clone());
6295        for id in ty.ty_params.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6296            if !id.is_empty() {
6297                idents.insert(id.to_string());
6298            }
6299        }
6300        // record 组件头原文 `(MouseEvent e, @Anno Comparator<X> c)`——
6301        // 组件类型对 AST 扫描不可见(InputJavadocMethodScopeAnonInner
6302        // 抓获:MouseEvent import 误删)
6303        for id in ty.header.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6304            if !id.is_empty() {
6305                idents.insert(id.to_string());
6306            }
6307        }
6308        for e in ty.extends.iter().chain(ty.implements.iter()) {
6309            for id in e.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6310                if !id.is_empty() {
6311                    idents.insert(id.to_string());
6312                }
6313            }
6314        }
6315        for m in &ty.members {
6316            match m {
6317                Member::Field { mods, ty, declarators } => {
6318                    collect_mods_idents(mods, &mut idents);
6319                    collect_ty_idents(ty, &mut idents);
6320                    for d in declarators {
6321                        idents.insert(d.name.clone());
6322                    }
6323                }
6324                Member::Method { mods, ret, name, params, throws, .. } => {
6325                    collect_mods_idents(mods, &mut idents);
6326                    collect_ty_idents(ret, &mut idents);
6327                    idents.insert(name.clone());
6328                    for p in params {
6329                        collect_mods_idents(&p.mods, &mut idents);
6330                        collect_ty_idents(&p.ty, &mut idents);
6331                        idents.insert(p.name.clone());
6332                    }
6333                    for t in throws {
6334                        for id in t.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6335                            if !id.is_empty() {
6336                                idents.insert(id.to_string());
6337                            }
6338                        }
6339                    }
6340                }
6341                Member::Constructor { mods, name, params, throws, .. } => {
6342                    collect_mods_idents(mods, &mut idents);
6343                    idents.insert(name.clone());
6344                    for p in params {
6345                        collect_mods_idents(&p.mods, &mut idents);
6346                        collect_ty_idents(&p.ty, &mut idents);
6347                        idents.insert(p.name.clone());
6348                    }
6349                    for t in throws {
6350                        for id in t.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6351                            if !id.is_empty() {
6352                                idents.insert(id.to_string());
6353                            }
6354                        }
6355                    }
6356                }
6357                Member::Type(t) => {
6358                    // 嵌套类型递归(for_each_type 已递归——这里不重复)
6359                    let _ = t;
6360                }
6361                _ => {}
6362            }
6363        }
6364    });
6365    // 3) 保留判定
6366    let before = unit.imports.len();
6367    unit.imports.retain(|imp| {
6368        if imp.contains('*') {
6369            return true; // on-demand 无法证明
6370        }
6371        // static import 的简单名可能是方法名(VarRef 收集覆盖),同规则处理
6372        let path = imp.trim_start_matches("static ").trim();
6373        let simple = path.rsplit('.').next().unwrap_or(path).trim();
6374        if simple.is_empty() || !simple.chars().next().is_some_and(|c| c.is_uppercase()) {
6375            return true; // 非类型形态(方法/常量 static import 简单名小写起)→ 保守保留
6376        }
6377        idents.contains(simple)
6378    });
6379    before - unit.imports.len()
6380}
6381
6382fn collect_ty_idents(t: &JType, out: &mut std::collections::HashSet<String>) {
6383    match t {
6384        JType::Ref(r) => {
6385            for id in r.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6386                if !id.is_empty() {
6387                    out.insert(id.to_string());
6388                }
6389            }
6390        }
6391        JType::Array(inner) => collect_ty_idents(inner, out),
6392        _ => {}
6393    }
6394}
6395
6396fn collect_mods_idents(mods: &str, out: &mut std::collections::HashSet<String>) {
6397    // 注解原文(@SuppressWarnings(...))——含参数里的类名引用
6398    if mods.contains('@') {
6399        for id in mods.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6400            if !id.is_empty() {
6401                out.insert(id.to_string());
6402            }
6403        }
6404    }
6405}
6406
6407/// 死私有字段删除:名字在全单元所有体内零值引用(VarRef 非 callee 位置
6408/// + Member 名)→ 删声明符;字段的声明符 init 必须是字面量/字面量数组
6409/// 或缺省(有副作用的 init 删不得——静态初始化顺序是可观察行为)。
6410/// 命名空间隔离:Call 的 callee VarRef 是方法名不是字段名(bd.java 的
6411/// 字段 e 与空方法 e 共存);this.e / A.e 的成员名也计入引用。
6412fn remove_dead_private_fields(ast: &JavaAst, unit: &mut CompilationUnit) -> usize {
6413    use std::collections::HashSet;
6414    let mut removed = 0usize;
6415    // 匿名类体/字符串字面量名字(引用扫描不可见)
6416    let anon_names = collect_anon_and_string_names(ast, unit);
6417    // 超集盲扫(68 个悬空字段根因:mods 注解实参 @Method(METHOD_NAME)、
6418    // 枚举体 .add(SHORT_NAME)、Raw 文本局部类)
6419    let blind = collect_all_blind_refs(ast, unit);
6420    // 1) 全单元字段名引用(值位置)
6421    let mut referenced: HashSet<String> = HashSet::new();
6422    let mut bodies: Vec<JavaId> = Vec::new();
6423    for_each_type(unit, &mut |ty: &TypeDecl| {
6424        for m in &ty.members {
6425            match m {
6426                Member::Method { body: Some(b), .. }
6427                | Member::Constructor { body: Some(b), .. }
6428                | Member::Initializer { body: b, .. } => bodies.push(*b),
6429                Member::Field { declarators, .. } => {
6430                    for d in declarators {
6431                        if let Some(init) = d.init {
6432                            bodies.push(init);
6433                        }
6434                    }
6435                }
6436                _ => {}
6437            }
6438        }
6439    });
6440    for root in bodies {
6441        // 栈携带 (节点, 是否为 Call 的 callee——方法命名空间,跳过)
6442        let mut stack: Vec<(JavaId, bool)> = vec![(root, false)];
6443        while let Some((n, is_callee)) = stack.pop() {
6444            match ast.kind(n) {
6445                NodeKind::VarRef if !is_callee => {
6446                    if let Some(nm) = ast.var_name(n) {
6447                        referenced.insert(nm.to_string());
6448                    }
6449                }
6450                NodeKind::Member => {
6451                    if let NodeData::Member { name: sym } = ast.data(n) {
6452                        referenced.insert(ast.sn(*sym).to_string());
6453                    }
6454                }
6455                _ => {}
6456            }
6457            if ast.kind(n) == NodeKind::Call {
6458                let ch = ast.children(n).to_vec();
6459                if let Some(&first) = ch.first() {
6460                    stack.push((first, true));
6461                }
6462                for &c in &ch[1..] {
6463                    stack.push((c, false));
6464                }
6465                continue;
6466            }
6467            for &c in ast.children(n) {
6468                stack.push((c, false));
6469            }
6470        }
6471    }
6472    // 2) 删除(可变借用阶段;嵌套类型递归):
6473    //    先逐成员清理声明符(字面量 init 或无 init 且零引用),再删空成员。
6474    //    serialVersionUID / serialPersistentFields 是 JVM 序列化按名反射
6475    //    读取的契约字段——「零源码引用」判据不适用(a9/a_ 抓获:删除后
6476    //    SUID 退化为结构计算值,旧流反序列化 InvalidClassException)
6477    for_each_type_mut(unit, &mut |ty: &mut TypeDecl| {
6478        for m in ty.members.iter_mut() {
6479            if let Member::Field { mods, declarators, .. } = m {
6480                let is_private = mods.split_whitespace().any(|w| w == "private");
6481                let annotated = mods.contains('@');
6482                if !is_private || annotated {
6483                    continue;
6484                }
6485                let dead: Vec<bool> = declarators
6486                    .iter()
6487                    .map(|d| {
6488                        let reflective =
6489                            d.name == "serialVersionUID" || d.name == "serialPersistentFields";
6490                        let init_ok =
6491                            d.init.map(|i| is_const_init(ast, i)).unwrap_or(true);
6492                        !reflective
6493                            && init_ok
6494                            && !referenced.contains(&d.name)
6495                            && !anon_names.contains(&d.name)
6496                            && !blind.contains(&d.name)
6497                    })
6498                    .collect();
6499                let before = declarators.len();
6500                let mut idx = 0usize;
6501                declarators.retain(|_| {
6502                    let keep = !dead[idx];
6503                    idx += 1;
6504                    keep
6505                });
6506                removed += before - declarators.len();
6507            }
6508        }
6509        let before = ty.members.len();
6510        ty.members
6511            .retain(|m| !matches!(m, Member::Field { declarators, .. } if declarators.is_empty()));
6512        removed += before - ty.members.len();
6513    });
6514    removed
6515}
6516
6517fn simplify_type(ast: &mut JavaAst, ty: &mut TypeDecl, cfg: &Config, total: &mut Report) {
6518    for m in &mut ty.members {
6519        simplify_member(ast, m, cfg, total);
6520    }
6521}
6522
6523fn simplify_member(ast: &mut JavaAst, m: &mut Member, cfg: &Config, total: &mut Report) {
6524    // 方法参数进入作用域基表,供类型解析使用
6525    let params: Vec<(String, JType)> = match m {
6526        Member::Method { params, .. } | Member::Constructor { params, .. } => params
6527            .iter()
6528            .map(|p| (p.name.clone(), p.ty.clone()))
6529            .collect(),
6530        _ => Vec::new(),
6531    };
6532    ast.set_param_scope(&params);
6533    match m {
6534        Member::Method { body: Some(b), .. }
6535        | Member::Constructor { body: Some(b), .. } => {
6536            let r = simplify(ast, *b, cfg);
6537            merge_report(total, r);
6538        }
6539        Member::Initializer { body, .. } => {
6540            let r = simplify(ast, *body, cfg);
6541            merge_report(total, r);
6542        }
6543        Member::Field { declarators, .. } => {
6544            for d in declarators.iter_mut() {
6545                if let Some(init) = d.init {
6546                    let r = simplify(ast, init, cfg);
6547                    merge_report(total, r);
6548                    // 根折叠:字段 init 节点就是 walk 根——Replace 无父
6549                    // 槽可写(引擎既有边界),由本侧拿到新根回写声明
6550                    //(static int x=1+2 曾永不折——本轮修复)
6551                    let rules = default_java_rules();
6552                    let mut cur: JavaId = init;
6553                    for _ in 0..8 {
6554                        match cure_engine::fold_root(ast, cur, &rules, cfg) {
6555                            Some((new_root, name)) => {
6556                                d.init = Some(new_root);
6557                                cur = new_root;
6558                                *total.by_rule.entry(name).or_insert(0) += 1;
6559                                total.edits += 1;
6560                                // 新根内部可能再折
6561                                let r = simplify(ast, new_root, cfg);
6562                                merge_report(total, r);
6563                            }
6564                            None => break,
6565                        }
6566                    }
6567                }
6568            }
6569        }
6570        Member::Type(t) => simplify_type(ast, t, cfg, total),
6571        Member::Raw(_) => {}
6572        _ => {}
6573    }
6574}
6575
6576fn merge_report(total: &mut Report, r: Report) {
6577    total.edits += r.edits;
6578    total.iterations += r.iterations;
6579    for (k, v) in r.by_rule {
6580        *total.by_rule.entry(k).or_insert(0) += v;
6581    }
6582}
6583
6584/// 默认配置。
6585pub fn default_config() -> Config {
6586    Config::default()
6587}