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