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        // 一般展平:构 2-child If 节点 + else 体语句追加到父 Block
4792        let parent = parent?;
4793        if lang.kind(parent) != NodeKind::Block {
4794            return None;
4795        }
4796        let idx = idx?;
4797        // 构新 If 节点([cond, then])——cond 是表达式不能裸插语句列表
4798        let new_if = lang.clone_node(id, vec![ch[0], then]);
4799        let mut insert: Vec<JavaId> = vec![new_if];
4800        // else 体语句追加(Block 展平或单语句直接加)
4801        if lang.kind(els) == NodeKind::Block {
4802            insert.extend(lang.children(els).to_vec());
4803        } else {
4804            insert.push(els);
4805        }
4806        Some(Edit::Splice {
4807            node: parent,
4808            index: idx,
4809            remove: 1,
4810            insert,
4811        })
4812    }
4813}
4814
4815pub struct TrailingIfContinue;
4816
4817impl Rule<JavaAst> for TrailingIfContinue {
4818    fn name(&self) -> &'static str {
4819        "trailing_if_continue"
4820    }
4821    fn kinds(&self) -> &'static [NodeKind] {
4822        &[NodeKind::If]
4823    }
4824    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4825        let parent = ctx.parent(id);
4826        let grandparent = parent.and_then(|p| ctx.parent(p));
4827        let lang = ctx.lang;
4828        if lang.kind(id) != NodeKind::If {
4829            return None;
4830        }
4831        let ch = lang.children(id).to_vec();
4832        // 恰两分支(cond, then——无 else)
4833        if ch.len() != 2 {
4834            return None;
4835        }
4836        let then = ch[1];
4837        if lang.kind(then) != NodeKind::Block {
4838            return None;
4839        }
4840        let tbody = lang.children(then).to_vec();
4841        if tbody.len() != 1 || lang.kind(tbody[0]) != NodeKind::Continue {
4842            return None;
4843        }
4844        // 无标签 continue(带标签的跳法不同)
4845        if !matches!(lang.data(tbody[0]), NodeData::Continue { label: None }) {
4846            return None;
4847        }
4848        // 父是块且本 if 是最后一条语句;父块是循环体(While/For/ForEach
4849        // 的最后一个孩子)
4850        let p = parent?;
4851        if lang.kind(p) != NodeKind::Block {
4852            return None;
4853        }
4854        let pch = lang.children(p).to_vec();
4855        if pch.last() != Some(&id) {
4856            return None;
4857        }
4858        // 祖父是循环(While/For/ForEach 的体)
4859        let gp = grandparent?;
4860        match lang.kind(gp) {
4861            NodeKind::While | NodeKind::For | NodeKind::ForEach | NodeKind::DoWhile => {
4862                let gch = lang.children(gp).to_vec();
4863                if gch.last() == Some(&p) {
4864                    Some(Edit::Delete { node: id })
4865                } else {
4866                    None
4867                }
4868            }
4869            _ => None,
4870        }
4871    }
4872}
4873
4874pub struct TrailingAssignReturn;
4875
4876impl Rule<JavaAst> for TrailingAssignReturn {
4877    fn name(&self) -> &'static str {
4878        "trailing_assign_return"
4879    }
4880    fn kinds(&self) -> &'static [NodeKind] {
4881        &[NodeKind::Assign]
4882    }
4883    // 节点数持平(Assign+Return 2 语句 → 1 语句)——按节点计数不减,
4884    // 价值在行数与后续连锁(if_to_ternary 等以 return 形态收敛)
4885    fn structural(&self) -> bool {
4886        true
4887    }
4888    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4889        let parent = ctx.parent(id);
4890        let grandparent = parent.and_then(|p| ctx.parent(p));
4891        let lang = ctx.lang;
4892        // 简单赋值(复合赋值不折)
4893        if !matches!(lang.data(id), NodeData::Assign { op: None }) {
4894            return None;
4895        }
4896        let ch = lang.children(id).to_vec();
4897        if ch.len() != 2 {
4898            return None;
4899        }
4900        let (target, value) = (ch[0], ch[1]);
4901        // 目标:局部(字段/参数回写惯用形排除——getter 回写是语义性的)
4902        if lang.kind(target) != NodeKind::VarRef || !lang.is_local_var(target) {
4903            return None;
4904        }
4905        let name = lang.var_name(target)?.to_string();
4906        // 赋值语句的父块(语句级:ExprStmt 包裹或裸 Assign)
4907        let parent = parent?;
4908        let stmt = if lang.kind(parent) == NodeKind::ExprStmt {
4909            parent
4910        } else if lang.kind(parent) == NodeKind::Block {
4911            id
4912        } else {
4913            return None;
4914        };
4915        let block = if lang.kind(parent) == NodeKind::ExprStmt {
4916            grandparent?
4917        } else {
4918            parent
4919        };
4920        if lang.kind(block) != NodeKind::Block {
4921            return None;
4922        }
4923        let stmts = lang.children(block).to_vec();
4924        let idx = stmts.iter().position(|&x| x == stmt)?;
4925        // 相邻下一条:return v / throw v
4926        let next = *stmts.get(idx + 1)?;
4927        let (flow_val, is_return) = match lang.kind(next) {
4928            NodeKind::Return => (lang.children(next).first().copied(), true),
4929            NodeKind::Throw => (lang.children(next).first().copied(), false),
4930            _ => return None,
4931        };
4932        let flow_val = flow_val?;
4933        if lang.kind(flow_val) != NodeKind::VarRef || lang.var_name(flow_val) != Some(name.as_str()) {
4934            return None;
4935        }
4936        // return 之后语句零 v 引用(死代码里的引用会因赋值消失失去
4937        // 定赋值而不可编译)
4938        for &s in &stmts[idx + 2..] {
4939            if cure_engine::analysis::subtree_contains(lang, s, |n| {
4940                lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name.as_str())
4941            }) {
4942                return None;
4943            }
4944        }
4945        // 折叠:下一条流语句替换为 return/throw expr;赋值语句删除
4946        let new_flow = if is_return {
4947            lang.ret(Some(value))
4948        } else {
4949            lang.throw(value)
4950        };
4951        Some(Edit::Multi(vec![
4952            Edit::Replace { target: next, with: new_flow },
4953            Edit::Delete { node: stmt },
4954        ]))
4955    }
4956}
4957
4958pub struct ConstFieldPropagate;
4959
4960impl Rule<JavaAst> for ConstFieldPropagate {
4961    fn name(&self) -> &'static str {
4962        "const_field_propagate"
4963    }
4964    fn kinds(&self) -> &'static [NodeKind] {
4965        &[NodeKind::VarRef]
4966    }
4967    // 结构性规则:VarRef→Literal 节点数持平(成本模型不增不减)——
4968    // 价值在语义透明(读点直接可读),非节点缩减
4969    fn structural(&self) -> bool {
4970        true
4971    }
4972    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
4973        let parent = ctx.parent(id);
4974        let _grandparent = parent.and_then(|p| ctx.parent(p));
4975        let lang = ctx.lang;
4976        if lang.kind(id) != NodeKind::VarRef {
4977            return None;
4978        }
4979        let name = lang.var_name(id)?.to_string();
4980        if !lang.const_scalars.contains(&name) {
4981            return None;
4982        }
4983        if let Some(p) = parent {
4984            let is_first = lang.children(p).first() == Some(&id);
4985            match lang.kind(p) {
4986                // 赋值目标(含复合赋值):保留原赋值
4987                NodeKind::Assign if is_first => return None,
4988                NodeKind::Unary if is_first => {
4989                    if let NodeData::Unary { op } = lang.data(p) {
4990                        if op.is_incdec() {
4991                            return None;
4992                        }
4993                    }
4994                }
4995                // 裸名调用 callee:方法名,不是字段读
4996                NodeKind::Call if is_first => return None,
4997                _ => {}
4998            }
4999        }
5000        let init = *lang.const_fields.get(&name)?;
5001        let lit = lang.copy_subtree(init);
5002        Some(Edit::Replace { target: id, with: lit })
5003    }
5004}
5005
5006pub struct NoopPrivateCall;
5007
5008impl Rule<JavaAst> for NoopPrivateCall {
5009    fn name(&self) -> &'static str {
5010        "noop_private_call"
5011    }
5012    fn kinds(&self) -> &'static [NodeKind] {
5013        &[NodeKind::ExprStmt]
5014    }
5015    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
5016        let lang = ctx.lang;
5017        if lang.kind(id) != NodeKind::ExprStmt {
5018            return None;
5019        }
5020        let ch = lang.children(id).to_vec();
5021        if ch.len() != 1 || lang.kind(ch[0]) != NodeKind::Call {
5022            return None;
5023        }
5024        let call = ch[0];
5025        let cch = lang.children(call).to_vec();
5026        if cch.is_empty() || lang.kind(cch[0]) != NodeKind::VarRef {
5027            return None; // 限定名调用(A.m / this.m)是别的方法解析域
5028        }
5029        let name = lang.var_name(cch[0])?.to_string();
5030        let arity = cch.len() - 1;
5031        let cands = lang.noop_private_methods.get(&name)?.get(&arity)?;
5032        if cands.is_empty() {
5033            return None;
5034        }
5035        // 类型适用性消解:实参静态类型(局部 var_type / 字段 field_types /
5036        // 字面量种类)对每个候选形参表判定;未知实参 → 该候选视为适用
5037        //(不能证不适用——保守)。唯一适用者且 noop → 删除语句。
5038        let arg_types: Vec<Option<JType>> =
5039            cch[1..].iter().map(|&a| static_arg_type(lang, a)).collect();
5040        let mut applicable_noop = false;
5041        let mut applicable_count = 0usize;
5042        for c in cands {
5043            let ok = c
5044                .params
5045                .iter()
5046                .zip(arg_types.iter())
5047                .all(|(pt, at)| arg_applicable(at.as_ref(), pt));
5048            if ok {
5049                applicable_count += 1;
5050                applicable_noop = c.noop;
5051            }
5052        }
5053        if applicable_count != 1 || !applicable_noop {
5054            return None;
5055        }
5056        // 实参效应 ≤ MayRead(VarRef/字面量/纯算术——字段读不抛不写)
5057        for &arg in &cch[1..] {
5058            if lang.effect(arg) > Effect::MayRead {
5059                return None;
5060            }
5061        }
5062        Some(Edit::Delete { node: id })
5063    }
5064}
5065
5066/// 实参静态类型(判定过载适用性用):局部/参数走 var_type 解析,字段
5067/// 走 field_types 表,字面量按种类。null → None(任何引用形参都可能——
5068/// 全候选适用,保守)。其余表达式 → None。
5069fn static_arg_type(lang: &JavaAst, arg: JavaId) -> Option<JType> {
5070    match lang.kind(arg) {
5071        NodeKind::Literal => match lang.literal(arg)? {
5072            LitRef::Str(_) => Some(JType::Ref("String".to_string())),
5073            LitRef::Char(_) => Some(JType::Char),
5074            LitRef::Bool(_) => Some(JType::Bool),
5075            LitRef::Int(_) => Some(JType::Int),
5076            LitRef::Long(_) => Some(JType::Long),
5077            LitRef::Float(_) => Some(JType::Float),
5078            LitRef::Double(_) => Some(JType::Double),
5079            LitRef::Null => None,
5080        },
5081        NodeKind::VarRef => {
5082            let nm = lang.var_name(arg)?;
5083            lang.var_type(arg)
5084                .cloned()
5085                .or_else(|| lang.field_types.get(nm).cloned())
5086        }
5087        _ => None,
5088    }
5089}
5090
5091/// 实参类型对形参的适用性(保守方向:不能证不适用 → true)。
5092/// JLS 过载解析的保守近似:精确简单名匹配 + Object 通配;装箱/拓宽
5093/// 判不适用(配合「唯一适用」判定仍安全:装箱场景要么 0 适用要么
5094/// 精确者胜出)。
5095fn arg_applicable(at: Option<&JType>, pt: &JType) -> bool {
5096    let Some(a) = at else { return true };
5097    match pt {
5098        JType::Ref(p) => {
5099            let pbase = p.split('<').next().unwrap_or(p).trim();
5100            pbase == "Object"
5101                || pbase == "java.lang.Object"
5102                || match a {
5103                    JType::Ref(arend) => {
5104                        let abase = arend.split('<').next().unwrap_or(arend).trim();
5105                        abase == pbase
5106                    }
5107                    _ => false,
5108                }
5109        }
5110        JType::Array(_) => false, // 简单名/字面量实参不适用数组形参(null → None 已兜底)
5111        prim => match a {
5112            p2 => std::mem::discriminant(p2) == std::mem::discriminant(prim),
5113        },
5114    }
5115}
5116
5117// ---------------------------------------------------------------------------
5118// 常量实参方法内联(解密 helper):d(0) → 方法体(参数→实参字面量替换)。
5119// 语义恒安全:单 return 表达式在调用点求值一次,两种形态一致;
5120// 自递归方法拒绝;实参须全为字面量。
5121// 【结构性规则】豁免成本门槛:内联暂时变贵、由后续常量折叠回本。
5122// 终止性:守卫"体内不含任何内联名的裸调用"——每次内联严格消费一个
5123// 此类调用点且不引入新的同类节点。
5124// ---------------------------------------------------------------------------
5125
5126pub struct ConstMethodInline;
5127
5128impl Rule<JavaAst> for ConstMethodInline {
5129    fn name(&self) -> &'static str {
5130        "const_method_inline"
5131    }
5132    fn kinds(&self) -> &'static [NodeKind] {
5133        &[NodeKind::Call]
5134    }
5135    fn structural(&self) -> bool {
5136        true
5137    }
5138    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
5139        // 语句位标志先取(ctx.lang 借用后 ctx 部分移动——R13 教训)
5140        let at_stmt_pos = ctx
5141            .parent(id)
5142            .map(|p| ctx.lang.kind(p) == NodeKind::ExprStmt)
5143            .unwrap_or(false);
5144        let lang = ctx.lang;
5145        if lang.kind(id) != NodeKind::Call {
5146            return None;
5147        }
5148        let ch = lang.children(id).to_vec();
5149        // recv_class:静态限定调用的接收者名(Type.valueOf 形态)。
5150        // 归属守卫的输入——None = 裸名调用(valueOf(x),隐式本类)或
5151        // this 前缀,不做归属判定。
5152        let (method_name, recv_class) = match lang.data(ch[0]) {
5153            NodeData::VarRef { name } => (*name, None),
5154            NodeData::Member { name } => {
5155                let recv = lang.children(ch[0])[0];
5156                match lang.var_name(recv) {
5157                    Some(r) if r == "this" => (*name, None),
5158                    Some(r) if r.chars().next().is_some_and(|c| c.is_uppercase()) => {
5159                        (*name, Some(r.to_string()))
5160                    }
5161                    _ => return None,
5162                }
5163            }
5164            _ => return None,
5165        };
5166        let entry = lang.inline_methods.get(lang.sn(method_name)).cloned()?;
5167        let (params, body_expr, owner) = entry;
5168        // **归属守卫**:限定调用的接收者类名必须等于声明类——此前名字
5169        // 匹配不判归属,把 JDK 的 `Integer.valueOf(-1)`(接收者大写即
5170        // 放行)误当本类 `valueOf(boolean)` 内联,代入 -1 产出
5171        // `!-1 ? Boolean.FALSE : Boolean.TRUE` 不可编译(ddc lark 语料
5172        // jna Function 复现)。裸名/this 调用在同一单元内由方法名唯一
5173        // 性计数守卫兜底。
5174        if let Some(recv) = recv_class {
5175            if recv != owner {
5176                return None;
5177            }
5178        }
5179        // 终止守卫:方法体内不得含任何【内联名】的裸调用
5180        {
5181            let names: Vec<String> = lang.inline_methods.keys().cloned().collect();
5182            for nm in &names {
5183                if subtree_calls_self(lang, body_expr, nm) {
5184                    return None;
5185                }
5186            }
5187        }
5188        let args = ch[1..].to_vec();
5189        if args.len() != params.len() || params.is_empty() {
5190            return None;
5191        }
5192        for &a in &args {
5193            lang.literal(a)?;
5194        }
5195        let map: std::collections::HashMap<String, JavaId> = params
5196            .iter()
5197            .zip(args.iter())
5198            .map(|(p, &a)| (p.clone(), a))
5199            .collect();
5200        let with = copy_subst(lang, body_expr, &map);
5201        // **语句位形态守卫**(ddc/deepseek cs8 抓获:`e("KType");` 的
5202        // 单 return 体内联成 `mk(...).a;`——字段访问不是合法表达式语句
5203        //(JLS 14.8 仅赋值/自增自减/调用/new),与 R12 foreach 的 e; 同
5204        // 族)。调用在裸 ExprStmt 位(结果弃置)时,替换表达式必须是
5205        // Call/New——否则保留原调用
5206        if at_stmt_pos {
5207            match lang.kind(with) {
5208                NodeKind::Call | NodeKind::New => {}
5209                _ => return None,
5210            }
5211        }
5212        Some(Edit::Replace {
5213            target: id,
5214            with,
5215        })
5216    }
5217}
5218
5219/// 深拷贝 + VarRef 参数替换。
5220fn copy_subst(
5221    lang: &mut JavaAst,
5222    node: JavaId,
5223    map: &std::collections::HashMap<String, JavaId>,
5224) -> JavaId {
5225    if let NodeData::VarRef { name } = lang.data(node) {
5226        if let Some(&repl) = map.get(lang.sn(*name)) {
5227            return lang.copy_subtree(repl);
5228        }
5229    }
5230    let children: Vec<JavaId> = lang.children(node).to_vec();
5231    let new_children: Vec<JavaId> = children
5232        .iter()
5233        .map(|&c| copy_subst(lang, c, map))
5234        .collect();
5235    lang.clone_node(node, new_children)
5236}
5237
5238
5239// ---------------------------------------------------------------------------
5240// try-with-resources 还原(twr_recover):两种反编译器直出形态。
5241//
5242// family B(ddc:javac→d8 字节码的紧凑渲染):
5243//   R r = new R(...);            // 资源(new——无卫语句形态下唯一非空证明)
5244//   try { B }
5245//   catch (Throwable t) {
5246//       try { r.close(); }
5247//       catch (Throwable sup) { t.addSuppressed(sup); }
5248//       throw t;
5249//   }
5250//   r.close();                   // 正常路径尾部直调
5251//   → try (R r = new R(...)) { B }
5252//
5253// family A(javac 源级翻译;CFR/ProGuard 常见):
5254//   R r = init; Throwable primary = null;
5255//   try { B }
5256//   catch (Throwable t) { primary = t; throw t; }
5257//   finally {
5258//       if (r != null) {
5259//           if (primary != null) {
5260//               try { r.close(); } catch (Throwable sup) { primary.addSuppressed(sup); }
5261//           } else { r.close(); }
5262//       }
5263//   }
5264//   → try (R r = init) { B }      // 含 null 资源跳过 close 的路径(卫语句保证)
5265//
5266// 多资源:嵌套形态由不动点逐层还原(内层先→外层 try 体含 try(res)——
5267// JLS 上嵌套 TWR ≡ 多资源 TWR,无需合并)。守卫:r/primary/t/sup 的全部
5268// 引用必须落在模式消费的子树内(否则不还原);同名遮蔽一律拒绝。
5269// ---------------------------------------------------------------------------
5270
5271pub struct TwrRecover;
5272
5273fn twr_is_throwable(ty_raw: &str) -> bool {
5274    ty_raw == "Throwable" || ty_raw == "java.lang.Throwable"
5275}
5276
5277/// 表达式语句解包:ExprStmt{e} → e(赋值/调用等语句位置)。
5278fn unwrap_expr_stmt(lang: &JavaAst, n: JavaId) -> JavaId {
5279    if lang.kind(n) == NodeKind::ExprStmt {
5280        if let Some(&e) = lang.children(n).first() {
5281            return e;
5282        }
5283    }
5284    n
5285}
5286
5287/// 语句序列(Block 展开;单语句原样)。
5288fn twr_stmts(lang: &JavaAst, n: JavaId) -> Vec<JavaId> {
5289    if lang.kind(n) == NodeKind::Block {
5290        lang.children(n).to_vec()
5291    } else {
5292        vec![n]
5293    }
5294}
5295
5296/// `r.close()` 调用表达式(ExprStmt 解包后)。
5297fn twr_is_close_call(lang: &JavaAst, e: JavaId, r: &str) -> bool {
5298    if lang.kind(e) != NodeKind::Call {
5299        return false;
5300    }
5301    let ch = lang.children(e);
5302    if ch.len() != 1 {
5303        return false;
5304    }
5305    match lang.data(ch[0]) {
5306        NodeData::Member { name } if lang.sn(*name) == "close" => {
5307            let Some(&recv) = lang.children(ch[0]).first() else {
5308                return false;
5309            };
5310            lang.kind(recv) == NodeKind::VarRef && lang.var_name(recv) == Some(r)
5311        }
5312        _ => false,
5313    }
5314}
5315
5316fn twr_is_close_stmt(lang: &JavaAst, n: JavaId, r: &str) -> bool {
5317    if lang.kind(n) != NodeKind::ExprStmt {
5318        return false;
5319    }
5320    lang.children(n)
5321        .first()
5322        .is_some_and(|&e| twr_is_close_call(lang, e, r))
5323}
5324
5325/// `primary.addSuppressed(sup)` 语句。
5326fn twr_is_add_suppressed(lang: &JavaAst, n: JavaId, primary: &str, sup: &str) -> bool {
5327    if lang.kind(n) != NodeKind::ExprStmt {
5328        return false;
5329    }
5330    let Some(&e) = lang.children(n).first() else {
5331        return false;
5332    };
5333    if lang.kind(e) != NodeKind::Call {
5334        return false;
5335    }
5336    let ch = lang.children(e);
5337    if ch.len() != 2 {
5338        return false;
5339    }
5340    match lang.data(ch[0]) {
5341        NodeData::Member { name } if lang.sn(*name) == "addSuppressed" => {
5342            let Some(&recv) = lang.children(ch[0]).first() else {
5343                return false;
5344            };
5345            lang.kind(recv) == NodeKind::VarRef
5346                && lang.var_name(recv) == Some(primary)
5347                && lang.kind(ch[1]) == NodeKind::VarRef
5348                && lang.var_name(ch[1]) == Some(sup)
5349        }
5350        _ => false,
5351    }
5352}
5353
5354fn node_contains(lang: &JavaAst, root: JavaId, target: JavaId) -> bool {
5355    let mut stack = vec![root];
5356    while let Some(n) = stack.pop() {
5357        if n == target {
5358            return true;
5359        }
5360        stack.extend(lang.children(n).iter().copied());
5361    }
5362    false
5363}
5364
5365/// root 内存在 name 的引用且不在任何 allowed 子树内(模式外的逃逸引用)。
5366fn refs_escape(lang: &JavaAst, root: JavaId, name: &str, allowed: &[JavaId]) -> bool {
5367    let mut stack = vec![root];
5368    while let Some(n) = stack.pop() {
5369        if lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name) {
5370            let ok = allowed
5371                .iter()
5372                .any(|&a| a == n || node_contains(lang, a, n));
5373            if !ok {
5374                return true;
5375            }
5376        }
5377        if lang.kind(n) == NodeKind::VarDecl {
5378            if let Some(nm) = lang.var_name(n) {
5379                if nm == name && !allowed.contains(&n) {
5380                    // 同名再声明(遮蔽风险)→ 拒绝
5381                    return true;
5382                }
5383            }
5384        }
5385        stack.extend(lang.children(n).iter().copied());
5386    }
5387    false
5388}
5389
5390/// Try{try 块, 单 catch(Throwable, sup), 无 finally} 且 catch 体 =
5391/// [addSuppressed(primary, sup)]、try 体 = [r.close()]。
5392/// 返回 (r 名, sup 名)。primary 传闭包外确定。
5393fn twr_close_with_suppressed(
5394    lang: &JavaAst,
5395    try_node: JavaId,
5396    primary: &str,
5397) -> Option<(String, String)> {
5398    let ch = lang.children(try_node).to_vec();
5399    if ch.len() != 2 || lang.kind(ch[0]) != NodeKind::Block {
5400        return None;
5401    }
5402    let NodeData::Catch { ty_raw, name: sup } = lang.data(ch[1]) else {
5403        return None;
5404    };
5405    if !twr_is_throwable(ty_raw) {
5406        return None;
5407    }
5408    let sup = sup.clone();
5409    let catch_stmts = twr_stmts(lang, *lang.children(ch[1]).first()?);
5410    if catch_stmts.len() != 1 || !twr_is_add_suppressed(lang, catch_stmts[0], primary, &sup) {
5411        return None;
5412    }
5413    let close_stmts = twr_stmts(lang, ch[0]);
5414    if close_stmts.len() != 1 {
5415        return None;
5416    }
5417    if lang.kind(close_stmts[0]) != NodeKind::ExprStmt {
5418        return None;
5419    }
5420    let e = *lang.children(close_stmts[0]).first()?;
5421    let cc = lang.children(e);
5422    if cc.len() != 1 {
5423        return None;
5424    }
5425    let NodeData::Member { name } = lang.data(cc[0]) else {
5426        return None;
5427    };
5428    if lang.sn(*name) != "close" {
5429        return None;
5430    }
5431    let recv = *lang.children(cc[0]).first()?;
5432    if lang.kind(recv) != NodeKind::VarRef {
5433        return None;
5434    }
5435    Some((lang.var_name(recv)?.to_string(), sup))
5436}
5437
5438impl Rule<JavaAst> for TwrRecover {
5439    fn name(&self) -> &'static str {
5440        "twr_recover"
5441    }
5442    fn kinds(&self) -> &'static [NodeKind] {
5443        &[NodeKind::Try]
5444    }
5445    fn structural(&self) -> bool {
5446        true
5447    }
5448    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
5449        // 先取 ctx 查询(lang 的 &mut 移出后 ctx 不可再用)
5450        let (parent, idx, walk_root) = (ctx.parent(id)?, ctx.index(id)?, ctx.root());
5451        let lang = ctx.lang;
5452        if lang.kind(id) != NodeKind::Try {
5453            return None;
5454        }
5455        let ch = lang.children(id).to_vec();
5456        // 资源已存在的(嵌套已还原)不再匹配
5457        let try_idx = ch
5458            .iter()
5459            .position(|&c| lang.kind(c) == NodeKind::Block)?;
5460        if try_idx != 0 {
5461            return None;
5462        }
5463        let try_block = ch[0];
5464        if lang.kind(parent) != NodeKind::Block {
5465            return None;
5466        }
5467        let pch = lang.children(parent).to_vec();
5468
5469        // ===== family B:[decl_r, try{B}catch, close] =====
5470        if ch.len() == 2 {
5471            if let NodeData::Catch { ty_raw, name: t } = lang.data(ch[1]) {
5472                if twr_is_throwable(ty_raw) {
5473                    let t = t.clone();
5474                    let catch_stmts = twr_stmts(lang, *lang.children(ch[1]).first()?);
5475                    if catch_stmts.len() == 2 {
5476                        // [Try{close,sup}, throw t]
5477                        let (inner_try, throw_stmt) = (catch_stmts[0], catch_stmts[1]);
5478                        let throw_ok = lang.kind(throw_stmt) == NodeKind::Throw
5479                            && lang
5480                                .children(throw_stmt)
5481                                .first()
5482                                .is_some_and(|&x| {
5483                                    lang.kind(x) == NodeKind::VarRef
5484                                        && lang.var_name(x) == Some(t.as_str())
5485                                });
5486                        if throw_ok && lang.kind(inner_try) == NodeKind::Try {
5487                            if let Some((r, sup)) =
5488                                twr_close_with_suppressed(lang, inner_try, t.as_str())
5489                            {
5490                                if idx >= 1 && idx + 1 < pch.len() {
5491                                    let decl_r = pch[idx - 1];
5492                                    let trailing = pch[idx + 1];
5493                                    if let NodeData::VarDecl { name, ty, .. } = lang.data(decl_r) {
5494                                        if name == &r {
5495                                            let (name, ty) = (name.clone(), ty.clone());
5496                                            let init =
5497                                                lang.children(decl_r).first().copied();
5498                                            if let Some(init) = init {
5499                                                // 无卫语句形态:init 须为 new(非空证明)
5500                                                let provably_new = lang.kind(init)
5501                                                    == NodeKind::New;
5502                                                let close_ok =
5503                                                    twr_is_close_stmt(lang, trailing, &r);
5504                                                if provably_new && close_ok {
5505                                                    // 引用逃逸扫描
5506                                                    let root = walk_root;
5507                                                    let r_ok = !refs_escape(
5508                                                        lang,
5509                                                        root,
5510                                                        &r,
5511                                                        &[
5512                                                            decl_r,
5513                                                            init,
5514                                                            *lang
5515                                                                .children(inner_try)
5516                                                                .first()
5517                                                                .unwrap_or(&inner_try),
5518                                                            trailing,
5519                                                            try_block,
5520                                                        ],
5521                                                    );
5522                                                    let t_ok = !refs_escape(
5523                                                        lang, root, &t,
5524                                                        &[throw_stmt, ch[1]],
5525                                                    );
5526                                                    let sup_ok = !refs_escape(
5527                                                        lang, root, &sup,
5528                                                        &[ch[1]],
5529                                                    );
5530                                                    if r_ok && t_ok && sup_ok {
5531                                                        let init_copy =
5532                                                            lang.copy_subtree(init);
5533                                                        let res = lang.var_decl(
5534                                                            &name, ty,
5535                                                            Some(init_copy),
5536                                                        );
5537                                                        let new_try = lang.try_(
5538                                                            vec![res],
5539                                                            try_block,
5540                                                            Vec::new(),
5541                                                            None,
5542                                                        );
5543                                                        return Some(Edit::Splice {
5544                                                            node: parent,
5545                                                            index: idx - 1,
5546                                                            remove: 3,
5547                                                            insert: vec![new_try],
5548                                                        });
5549                                                    }
5550                                                }
5551                                            }
5552                                        }
5553                                    }
5554                                }
5555                            }
5556                        }
5557                    }
5558                }
5559            }
5560        }
5561
5562        // ===== family A:[decl_r, decl_primary, try{B}catch{primary=t;throw}finally{卫语句}] =====
5563        if ch.len() == 3 {
5564            let NodeData::Catch { ty_raw, name: t } = lang.data(ch[1]) else {
5565                return None;
5566            };
5567            if !twr_is_throwable(ty_raw) {
5568                return None;
5569            }
5570            let t = t.clone();
5571            // finally 块
5572            if lang.kind(ch[2]) != NodeKind::Block {
5573                return None;
5574            }
5575            let fin_stmts = twr_stmts(lang, ch[2]);
5576            if fin_stmts.len() != 1 || lang.kind(fin_stmts[0]) != NodeKind::If {
5577                return None;
5578            }
5579            // catch 体 = [primary = t, throw t]
5580            let catch_stmts = twr_stmts(lang, *lang.children(ch[1]).first()?);
5581            if catch_stmts.len() != 2 {
5582                return None;
5583            }
5584            let (assign_stmt, throw_stmt) = (catch_stmts[0], catch_stmts[1]);
5585            let assign_expr = unwrap_expr_stmt(lang, assign_stmt);
5586            let NodeData::Assign { op: None } = lang.data(assign_expr) else {
5587                return None;
5588            };
5589            let as_ch = lang.children(assign_expr);
5590            if as_ch.len() != 2 {
5591                return None;
5592            }
5593            let primary = match lang.data(as_ch[0]) {
5594                NodeData::VarRef { name } => lang.sn(*name).to_string(),
5595                _ => return None,
5596            };
5597            let assign_ok = lang.kind(as_ch[1]) == NodeKind::VarRef
5598                && lang.var_name(as_ch[1]) == Some(t.as_str())
5599                && lang.kind(as_ch[0]) == NodeKind::VarRef
5600                && lang.var_name(as_ch[0]) == Some(primary.as_str());
5601            let throw_ok = lang.kind(throw_stmt) == NodeKind::Throw
5602                && lang.children(throw_stmt)
5603                    .first()
5604                    .is_some_and(|&x| {
5605                        lang.kind(x) == NodeKind::VarRef
5606                            && lang.var_name(x) == Some(t.as_str())
5607                    });
5608            if !assign_ok || !throw_ok {
5609                return None;
5610            }
5611            // 卫语句:if (r != null) { if (primary != null) { try{r.close()}catch{sup} } else { r.close() } }
5612            let if1 = fin_stmts[0];
5613            let i1 = lang.children(if1).to_vec();
5614            if i1.len() < 2 {
5615                return None;
5616            }
5617            let cond1 = i1[0];
5618            let NodeData::Binary { op: BinOp::Ne } = lang.data(cond1) else {
5619                return None;
5620            };
5621            let c1 = lang.children(cond1);
5622            let r = match lang.data(c1[0]) {
5623                NodeData::VarRef { name } if lang.literal(c1[1]) == Some(LitRef::Null) => {
5624                    lang.sn(*name).to_string()
5625                }
5626                _ => {
5627                    return None;
5628                }
5629            };
5630            let then1 = i1[1];
5631            let inner_stmts = twr_stmts(lang, then1);
5632            if inner_stmts.len() != 1 || lang.kind(inner_stmts[0]) != NodeKind::If {
5633                return None;
5634            }
5635            let if2 = inner_stmts[0];
5636            let i2 = lang.children(if2).to_vec();
5637            if i2.len() != 3 {
5638                return None;
5639            }
5640            let NodeData::Binary { op: BinOp::Ne } = lang.data(i2[0]) else {
5641                return None;
5642            };
5643            let c2 = lang.children(i2[0]);
5644            let primary_ok = lang.kind(c2[0]) == NodeKind::VarRef
5645                && lang.var_name(c2[0]) == Some(primary.as_str())
5646                && lang.literal(c2[1]) == Some(LitRef::Null);
5647            if !primary_ok {
5648                return None;
5649            }
5650            let (then2, els2) = (i2[1], i2[2]);
5651            // then2: Try{[r.close()], catch{primary.addSuppressed(sup)}}
5652            let t2_stmts = twr_stmts(lang, then2);
5653            if t2_stmts.len() != 1 || lang.kind(t2_stmts[0]) != NodeKind::Try {
5654                return None;
5655            }
5656            let Some((r2, sup)) =
5657                twr_close_with_suppressed(lang, t2_stmts[0], primary.as_str())
5658            else {
5659                return None;
5660            };
5661            if r2 != r {
5662                return None;
5663            }
5664            // els2: [r.close()]
5665            let e2_stmts = twr_stmts(lang, els2);
5666            if e2_stmts.len() != 1 || !twr_is_close_stmt(lang, e2_stmts[0], &r) {
5667                return None;
5668            }
5669            // 父块三连:decl_r, decl_primary, try
5670            if idx < 2 {
5671                return None;
5672            }
5673            let decl_r = pch[idx - 2];
5674            let decl_primary = pch[idx - 1];
5675            let (r_decl, primary_decl) = match (lang.data(decl_r), lang.data(decl_primary)) {
5676                (
5677                    NodeData::VarDecl { name: rn, .. },
5678                    NodeData::VarDecl { name: pn, .. },
5679                ) => (rn.clone(), pn.clone()),
5680                _ => return None,
5681            };
5682            if r_decl != r || primary_decl != primary {
5683                return None;
5684            }
5685            // primary 初始化为 null
5686            let pinit_null = lang
5687                .children(decl_primary)
5688                .first()
5689                .is_some_and(|&n| lang.literal(n) == Some(LitRef::Null));
5690            if !pinit_null {
5691                return None;
5692            }
5693            let r_init = lang.children(decl_r).first().copied()?;
5694            let ty = match lang.data(decl_r) {
5695                NodeData::VarDecl { ty, .. } => ty.clone(),
5696                _ => return None,
5697            };
5698            // 引用逃逸扫描
5699            let root = walk_root;
5700            // finally 整体被消费:卫语句内的 r/primary 引用均合法
5701            let r_allowed = [decl_r, r_init, ch[2], try_block];
5702            let t_allowed = [throw_stmt, assign_stmt, ch[1], ch[2]];
5703            let p_allowed = [decl_primary, assign_stmt, ch[2]];
5704            if refs_escape(lang, root, &r, &r_allowed)
5705                || refs_escape(lang, root, &primary, &p_allowed)
5706                || refs_escape(lang, root, &t, &t_allowed)
5707                || refs_escape(lang, root, &sup, &[ch[2]])
5708            {
5709                return None;
5710            }
5711            let init_copy = lang.copy_subtree(r_init);
5712            let res = lang.var_decl(&r, ty, Some(init_copy));
5713            let new_try = lang.try_(vec![res], try_block, Vec::new(), None);
5714            return Some(Edit::Splice {
5715                node: parent,
5716                index: idx - 2,
5717                remove: 3,
5718                insert: vec![new_try],
5719            });
5720        }
5721        None
5722    }
5723}
5724
5725// ---------------------------------------------------------------------------
5726// 字符串 switch 还原(string_switch_recover):javac v7 索引二级 switch
5727// 形态(ddc 直出;javac 字节码本相——hashCode 定位 + equals 守卫 + 索引
5728// switch 分发):
5729//   int v7 = 0;
5730//   switch (s.hashCode()) {
5731//       case H_i: {
5732//           boolean e = s.equals("L_i");
5733//           if (!e) { break; } else {
5734//               v7 = K_i;
5735//               switch (v7) { case K_1: BODY_1; ... default: BODY_DEF; }
5736//           }
5737//       } ...
5738//   }
5739//   v7 = -1;
5740//   switch (v7) { case K_1: BODY_1; ... default: BODY_DEF; }   // 无匹配路径
5741//   → switch (s) { case "L_1": BODY_1; ... default: BODY_DEF; }
5742//
5743// 守卫:选择器与 equals 接收方同为纯 VarRef(一次求值语义);
5744// 各索引 switch(含尾部)签名全同;每个 BODY 以终止语句收尾(无落穿,
5745// 标签序无关);v/e 引用全部落在被消费子树内;H_i ≡ "L_i".hashCode()。
5746// 哈希碰撞(同 case 双守卫)形态更复杂 → 整体不还原(负控锁定)。
5747// ---------------------------------------------------------------------------
5748
5749pub struct StringSwitchRecover;
5750
5751fn subtree_sig(lang: &JavaAst, n: JavaId, out: &mut String) {
5752    out.push_str(&format!("{:?} ", lang.data(n)));
5753    for &c in lang.children(n) {
5754        subtree_sig(lang, c, out);
5755    }
5756}
5757
5758/// 从 v7 索引 switch 提取 case K → 语句、default → 语句。
5759fn ssw_bodies(lang: &JavaAst, sw: JavaId) -> Option<Vec<(Option<i64>, Vec<JavaId>)>> {
5760    let ch = lang.children(sw).to_vec();
5761    let mut out = Vec::new();
5762    for &c in &ch[1..] {
5763        let NodeData::Case { labels, is_default, .. } = lang.data(c) else {
5764            return None;
5765        };
5766        let is_default = *is_default;
5767        let cc = lang.children(c);
5768        let label = if is_default {
5769            None
5770        } else {
5771            if *labels != 1 {
5772                return None; // 多标签 → 复杂形态,不还原
5773            }
5774            let lab = cc[0];
5775            let lit = lang.literal(lab)?;
5776            let v = match lit {
5777                LitRef::Int(v) | LitRef::Long(v) => v,
5778                _ => return None, // 非整型标签(字符串 case 是还原产物/负控)
5779            };
5780            Some(v)
5781        };
5782        out.push((label, cc[*labels as usize..].to_vec()));
5783    }
5784    Some(out)
5785}
5786
5787fn ssw_body_terminal(lang: &JavaAst, stmts: &[JavaId]) -> bool {
5788    let Some(&last) = stmts.last() else {
5789        return false; // 空体无终止
5790    };
5791    let last = if lang.kind(last) == NodeKind::Block {
5792        match lang.children(last).last() {
5793            Some(&l) => l,
5794            None => return false,
5795        }
5796    } else {
5797        last
5798    };
5799    matches!(
5800        lang.kind(last),
5801        NodeKind::Return | NodeKind::Break | NodeKind::Throw | NodeKind::Continue
5802    )
5803}
5804
5805impl Rule<JavaAst> for StringSwitchRecover {
5806    fn name(&self) -> &'static str {
5807        "string_switch_recover"
5808    }
5809    fn kinds(&self) -> &'static [NodeKind] {
5810        &[NodeKind::Switch]
5811    }
5812    fn structural(&self) -> bool {
5813        true
5814    }
5815    fn check(&self, ctx: RewriteCtx<'_, JavaAst>, id: JavaId) -> Option<Edit<JavaAst>> {
5816        let (parent, idx, ctx_root) = (ctx.parent(id)?, ctx.index(id)?, ctx.root());
5817        let lang = ctx.lang;
5818        if lang.kind(id) != NodeKind::Switch {
5819            return None;
5820        }
5821        let ch = lang.children(id).to_vec();
5822        if ch.len() < 2 {
5823            return None;
5824        }
5825        // 选择器 x.hashCode()
5826        let x = match lang.data(ch[0]) {
5827            NodeData::Call => {
5828                let cc = lang.children(ch[0]);
5829                if cc.len() != 1 {
5830                    return None;
5831                }
5832                match lang.data(cc[0]) {
5833                    NodeData::Member { name } if lang.sn(*name) == "hashCode" => {
5834                        let recv = *lang.children(cc[0]).first()?;
5835                        if lang.kind(recv) != NodeKind::VarRef {
5836                            return None;
5837                        }
5838                        lang.var_name(recv)?.to_string()
5839                    }
5840                    _ => return None,
5841                }
5842            }
5843            _ => {
5844                return None;
5845            }
5846        };
5847        // 各 hash case:单整型标签 + [decl_e?, if(!guard) break else {v=K, switch(v)}]
5848        struct CaseInfo {
5849            lit: String,
5850            k: i64,
5851            inner_switch: JavaId,
5852        }
5853        let mut infos: Vec<CaseInfo> = Vec::new();
5854        let mut v_name: Option<String> = None;
5855        let mut v_nodes: Vec<JavaId> = Vec::new(); // v 的全部合法出现(供逃逸扫描)
5856        let mut guard_nodes: Vec<JavaId> = Vec::new();
5857        for &c in &ch[1..] {
5858            let NodeData::Case { labels, is_default, .. } = lang.data(c) else {
5859                return None;
5860            };
5861            let is_default = *is_default;
5862            if is_default {
5863                return None; // hash switch 不应有 default(无匹配由尾部处理)
5864            }
5865            if *labels != 1 {
5866                return None;
5867            }
5868            let cc = lang.children(c);
5869            let h = match lang.literal(cc[0])? {
5870                LitRef::Int(v) | LitRef::Long(v) => v as i32,
5871                _ => return None,
5872            };
5873            let body = cc[1..].to_vec();
5874            // 单 Block 解包
5875            let body = if body.len() == 1 && lang.kind(body[0]) == NodeKind::Block {
5876                lang.children(body[0]).to_vec()
5877            } else {
5878                body
5879            };
5880            let mut rest = body;
5881            // 可选物化 boolean
5882            let mut _e_decl: Option<(String, JavaId)> = None;
5883            if let Some(&first) = rest.first() {
5884                if let NodeData::VarDecl { name, .. } = lang.data(first) {
5885                    if let Some(&init) = lang.children(first).first() {
5886                        if is_x_equals(lang, init, &x) {
5887                            _e_decl = Some((name.clone(), first));
5888                            rest = rest[1..].to_vec();
5889                        }
5890                    }
5891                }
5892            }
5893            if rest.len() != 1 || lang.kind(rest[0]) != NodeKind::If {
5894                return None;
5895            }
5896            let ifn = rest[0];
5897            let ic = lang.children(ifn).to_vec();
5898            if ic.len() != 3 {
5899                return None;
5900            }
5901            // cond = !guard(guard = e 或 x.equals(L))
5902            let NodeData::Unary { op: UnOp::Not } = lang.data(ic[0]) else {
5903                return None;
5904            };
5905            let guard = *lang.children(ic[0]).first()?;
5906            let lit = match lang.data(guard) {
5907                NodeData::VarRef { name } => {
5908                    // 物化 boolean:其唯一定义须是 x.equals("L")
5909                    let Some((en, decl)) = &_e_decl else {
5910                        return None;
5911                    };
5912                    if lang.sn(*name) != en {
5913                        return None;
5914                    }
5915                    let init = *lang.children(*decl).first()?;
5916                    equals_lit(lang, init, &x)?
5917                }
5918                NodeData::Call => equals_lit(lang, guard, &x)?,
5919                _ => return None,
5920            };
5921            guard_nodes.push(guard);
5922            if let Some((_, decl)) = &_e_decl {
5923                guard_nodes.push(*decl);
5924            }
5925            // then = break(无标签)
5926            let then_ok = twr_stmts(lang, ic[1]).len() == 1
5927                && matches!(lang.data(twr_stmts(lang, ic[1])[0]),
5928                    NodeData::Break { label: None });
5929            if !then_ok {
5930                return None;
5931            }
5932            // els = [v = K, switch(v)] 或 [switch(K)]
5933            let els = twr_stmts(lang, ic[2]);
5934            let (k, inner_switch, assign_node) = match els.len() {
5935                2 => {
5936                    let assign_expr = unwrap_expr_stmt(lang, els[0]);
5937                    let NodeData::Assign { op: None } = lang.data(assign_expr) else {
5938                        return None;
5939                    };
5940                    let a_ch = lang.children(assign_expr);
5941                    let vn = match lang.data(a_ch[0]) {
5942                        NodeData::VarRef { name } => lang.sn(*name).to_string(),
5943                        _ => return None,
5944                    };
5945                    let k = match lang.literal(a_ch[1])? {
5946                        LitRef::Int(v) | LitRef::Long(v) => v,
5947                        _ => return None,
5948                    };
5949                    if lang.kind(els[1]) != NodeKind::Switch {
5950                        return None;
5951                    }
5952                    // switch 选择器须为 v
5953                    let sel = *lang.children(els[1]).first()?;
5954                    if lang.kind(sel) != NodeKind::VarRef
5955                        || lang.var_name(sel) != Some(vn.as_str())
5956                    {
5957                        return None;
5958                    }
5959                    match &v_name {
5960                        Some(v) if *v != vn => return None,
5961                        _ => v_name = Some(vn),
5962                    }
5963                    (k, els[1], Some(els[0]))
5964                }
5965                1 => {
5966                    if lang.kind(els[0]) != NodeKind::Switch {
5967                        return None;
5968                    }
5969                    // 常量折叠后的形态:switch(K)
5970                    let sel = *lang.children(els[0]).first()?;
5971                    let k = match lang.literal(sel)? {
5972                        LitRef::Int(v) | LitRef::Long(v) => v,
5973                        _ => return None,
5974                    };
5975                    (k, els[0], None)
5976                }
5977                _ => return None,
5978            };
5979            if let Some(a) = assign_node {
5980                v_nodes.push(a);
5981            }
5982            v_nodes.push(*lang.children(inner_switch).first()?);
5983            // H ≡ hash(L)
5984            let hlit: i32 = lit
5985                .chars()
5986                .fold(0i32, |acc, ch| acc.wrapping_mul(31).wrapping_add(ch as i32));
5987            if hlit != h {
5988                return None;
5989            }
5990            let _ = assign_node;
5991            infos.push(CaseInfo {
5992                lit,
5993                k,
5994                inner_switch,
5995            });
5996        }
5997        if infos.is_empty() {
5998            return None;
5999        }
6000        // 父块 + 尾部
6001        if lang.kind(parent) != NodeKind::Block {
6002            return None;
6003        }
6004        let pch = lang.children(parent).to_vec();
6005        let mut k = idx + 1;
6006        // 可选 v = -1
6007        if k < pch.len() {
6008            let texpr = unwrap_expr_stmt(lang, pch[k]);
6009            if let NodeData::Assign { op: None } = lang.data(texpr) {
6010                let a_ch = lang.children(texpr);
6011                if a_ch.len() == 2
6012                    && lang.kind(a_ch[0]) == NodeKind::VarRef
6013                    && lang.var_name(a_ch[0]) == v_name.as_deref()
6014                    && lang.literal(a_ch[1]) == Some(LitRef::Int(-1))
6015                {
6016                    v_nodes.push(pch[k]);
6017                    k += 1;
6018                }
6019            }
6020        }
6021        if k >= pch.len() || lang.kind(pch[k]) != NodeKind::Switch {
6022            return None;
6023        }
6024        let trailing = pch[k];
6025        // 尾部选择器:VarRef v 或常量
6026        let tsel = *lang.children(trailing).first()?;
6027        match lang.data(tsel) {
6028            NodeData::VarRef { name } if Some(lang.sn(*name)) == v_name.as_deref() => {
6029                v_nodes.push(tsel);
6030            }
6031            NodeData::Literal(Lit::Int(_)) | NodeData::Literal(Lit::Long(_)) => {}
6032            _ => {
6033                lang.literal(tsel)?;
6034            }
6035        }
6036        // 签名一致性:所有 inner switch + trailing 的**case 体**全同
6037        //(选择器除外——尾部的 v 常被赋值传播折叠成字面量)
6038        fn switch_cases_sig(lang: &JavaAst, sw: JavaId, out: &mut String) {
6039            for &c in &lang.children(sw)[1..] {
6040                subtree_sig(lang, c, out);
6041            }
6042        }
6043        let mut sig0 = String::new();
6044        switch_cases_sig(lang, infos[0].inner_switch, &mut sig0);
6045        for info in &infos[1..] {
6046            let mut sig = String::new();
6047            switch_cases_sig(lang, info.inner_switch, &mut sig);
6048            if sig != sig0 {
6049                return None;
6050            }
6051        }
6052        {
6053            let mut sig = String::new();
6054            switch_cases_sig(lang, trailing, &mut sig);
6055            if sig != sig0 {
6056                return None;
6057            }
6058        }
6059        // 提取 body(用第一个 inner switch)
6060        let bodies = ssw_bodies(lang, infos[0].inner_switch)?;
6061        let mut body_map: std::collections::HashMap<i64, Vec<JavaId>> =
6062            std::collections::HashMap::new();
6063        let mut default_body: Option<Vec<JavaId>> = None;
6064        for (lab, stmts) in &bodies {
6065            match lab {
6066                Some(v) => {
6067                    body_map.insert(*v, stmts.clone());
6068                }
6069                None => default_body = Some(stmts.clone()),
6070            }
6071        }
6072        let Some(default_body) = default_body else {
6073            return None;
6074        };
6075        // 终止性
6076        for stmts in body_map.values().chain(std::iter::once(&default_body)) {
6077            if !ssw_body_terminal(lang, stmts) {
6078                return None;
6079            }
6080        }
6081        // v/e 逃逸扫描(v 的声明本身合法——索引变量;其引用全在消费子树内)
6082        let root = ctx_root;
6083        if let Some(vn) = &v_name {
6084            let mut allowed: Vec<JavaId> = v_nodes.clone();
6085            allowed.push(trailing);
6086            if idx >= 1 {
6087                let cand = pch[idx - 1];
6088                if let NodeData::VarDecl { name, .. } = lang.data(cand) {
6089                    if name == vn {
6090                        allowed.push(cand);
6091                        // 声明位置紧邻(ddc 形态);隔开的声明仍拒绝
6092                        if idx != 1 {
6093                            allowed.pop();
6094                        }
6095                    }
6096                }
6097            }
6098            if refs_escape(lang, root, vn, &allowed) {
6099                return None;
6100            }
6101        }
6102        // 构建 switch (x) { case "L": BODY_K; ... default: BODY_DEF }
6103        let mut new_cases: Vec<JavaId> = Vec::new();
6104        for info in &infos {
6105            let Some(body) = body_map.get(&info.k) else {
6106                return None;
6107            };
6108            let lab = lang.lit(Lit::Str(info.lit.clone()));
6109            let case = lang.case_(vec![lab], false, false, body.clone());
6110            new_cases.push(case);
6111        }
6112        let def_case = lang.case_(Vec::new(), true, false, default_body);
6113        new_cases.push(def_case);
6114        let sel = lang.var(&x);
6115        let new_switch = lang.switch_(sel, new_cases);
6116        // 消费 [hash switch, (v=-1)?, trailing switch]
6117        let remove = k - idx + 1;
6118        Some(Edit::Splice {
6119            node: parent,
6120            index: idx,
6121            remove,
6122            insert: vec![new_switch],
6123        })
6124    }
6125}
6126
6127/// Call{x.equals("L")} → L
6128fn equals_lit(lang: &JavaAst, e: JavaId, x: &str) -> Option<String> {
6129    if lang.kind(e) != NodeKind::Call {
6130        return None;
6131    }
6132    let cc = lang.children(e);
6133    if cc.len() != 2 {
6134        return None;
6135    }
6136    match lang.data(cc[0]) {
6137        NodeData::Member { name } if lang.sn(*name) == "equals" => {
6138            let recv = *lang.children(cc[0]).first()?;
6139            if lang.kind(recv) != NodeKind::VarRef || lang.var_name(recv) != Some(x) {
6140                return None;
6141            }
6142            match lang.literal(cc[1])? {
6143                LitRef::Str(s) => Some(s.to_string()),
6144                _ => None,
6145            }
6146        }
6147        _ => None,
6148    }
6149}
6150
6151fn is_x_equals(lang: &JavaAst, e: JavaId, x: &str) -> bool {
6152    lang.kind(e) == NodeKind::Call && equals_lit(lang, e, x).is_some()
6153}
6154
6155// ---------------------------------------------------------------------------
6156// 门面
6157// ---------------------------------------------------------------------------
6158
6159
6160/// Java 默认规则集(引擎通用规则 + Java 特有规则;保守集,不含 DCE 类)。
6161pub fn default_java_rules() -> Vec<Box<dyn Rule<JavaAst>>> {
6162    let mut rules: Vec<Box<dyn Rule<JavaAst>>> = cure_engine::rules::default_rules();
6163    rules.push(Box::new(CastSimplify));
6164    rules.push(Box::new(SelfCompare));
6165    rules.push(Box::new(StringBuilderFold));
6166    rules.push(Box::new(BoxUnboxChain));
6167    rules.push(Box::new(IteratorToForEach));
6168    rules.push(Box::new(NewStringFold));
6169    rules.push(Box::new(NewStringCharArrayFold));
6170    rules.push(Box::new(EmptyFinallyStrip));
6171    rules.push(Box::new(TryUnwrapNoCatch));
6172    rules.push(Box::new(TryUnwrapRethrow));
6173    rules.push(Box::new(StaticExec));
6174    rules.push(Box::new(LoopHeadBreak));
6175    rules.push(Box::new(WhileIteratorToForEach));
6176    rules.push(Box::new(ConcatValueOfDrop));
6177    rules.push(Box::new(StringBuilderStatements));
6178    rules.push(Box::new(TrailingContinueJava));
6179    rules.push(Box::new(XorNoise));
6180    rules.push(Box::new(StrLenFold));
6181    rules.push(Box::new(LiteralEval));
6182    rules.push(Box::new(Base64NewStringFold));
6183    rules.push(Box::new(UrlDecodeFold));
6184    rules.push(Box::new(CffRecover));
6185    rules.push(Box::new(StaticArrayIndexFold));
6186    rules.push(Box::new(TrailingAssignReturn));
6187    rules.push(Box::new(DeadPureCall));
6188    rules.push(Box::new(CtorDelegationHoist));
6189    rules.push(Box::new(TrailingElseFlatten));
6190    rules.push(Box::new(TrailingIfContinue));
6191    rules.push(Box::new(ConstFieldPropagate));
6192    rules.push(Box::new(NoopPrivateCall));
6193    rules.push(Box::new(ConstMethodInline));
6194    rules.push(Box::new(TwrRecover));
6195    rules.push(Box::new(StringSwitchRecover));
6196    rules
6197}
6198
6199/// 全量规则(含 DCE 类选配项:不可达语句删除等)。
6200pub fn all_java_rules() -> Vec<Box<dyn Rule<JavaAst>>> {
6201    let mut rules = default_java_rules();
6202    let mut engine_all = cure_engine::rules::all_rules();
6203    rules.append(&mut engine_all);
6204    rules
6205}
6206
6207/// 对方法体(或任意语句 Block)运行 Java 规则集直到 fixed point。
6208pub fn simplify(ast: &mut JavaAst, root: JavaId, cfg: &Config) -> Report {
6209    let rules = default_java_rules();
6210    cure_engine::simplify(ast, root, &rules, cfg)
6211}
6212
6213/// 整个编译单元:逐个方法体/初始化块/字段初始化器优化,签名不动。
6214/// 带外部事实的单元简化:前端 IR 权威陈述(单定义/单使用/布尔性/
6215/// 调用副作用)注入后运行常规管线——事实只放宽守卫,不改变规则集。
6216/// 单元结束自动清空(JavaFacts 生命周期 = 编译单元)。
6217pub fn simplify_unit_with_facts(
6218    ast: &mut JavaAst,
6219    unit: &mut CompilationUnit,
6220    cfg: &Config,
6221    facts: &cure_java_ast::JavaFacts,
6222) -> Report {
6223    ast.set_facts(facts.clone());
6224    let r = simplify_unit(ast, unit, cfg);
6225    ast.clear_facts();
6226    r
6227}
6228
6229pub fn simplify_unit(ast: &mut JavaAst, unit: &mut CompilationUnit, cfg: &Config) -> Report {
6230    let mut total = Report::default();
6231    // 收敛外循环:引擎单次调用跑至"本轮无应用"即停,但末轮被丢弃的结构
6232    // 提案(目标父在搬移后过期,需新鲜 walk 复活)会漏到下一次调用——
6233    // 外层重跑到不动点(上限防乒乓;真实语料 1-2 轮清零,javaparser 6 个
6234    // 幂等失败文件抓获:const_method_inline/if_else_empty 二轮仍有编辑)
6235    for _cycle in 0..6 {
6236        let mut round = Report::default();
6237        collect_unit_consts(ast, unit);
6238        // no-op 调用清理表(--dead-code 门控):off 时清空——规则查询
6239        // 空表自然不触发
6240        if cfg.remove_dead_methods {
6241            collect_noop_private_methods(ast, unit);
6242        } else {
6243            ast.noop_private_methods.clear();
6244        }
6245        for ty in &mut unit.types {
6246            simplify_type(ast, ty, cfg, &mut round);
6247        }
6248        if cfg.remove_dead_methods {
6249            round.edits += remove_dead_private_fields(ast, unit);
6250            round.edits += remove_dead_private_methods(ast, unit);
6251            round.edits += remove_unused_imports(ast, unit);
6252        }
6253        total.edits += round.edits;
6254        total.iterations += round.iterations;
6255        total.discarded += round.discarded;
6256        for (k, v) in round.by_rule {
6257            *total.by_rule.entry(k).or_insert(0) += v;
6258        }
6259        // 注:确认轮不可省——mega 实测驳回过"零丢弃即不动点"短路:跨轮
6260        // 编辑的主来源是 collect 表过期(内联/折叠后新常量/新单 return 方法
6261        // 涌现,重建表后才可见),非引擎丢弃提案的复活。RSS +2-3MB 是
6262        // 收敛正确性的价格。
6263        if round.edits == 0 {
6264            break;
6265        }
6266    }
6267    total
6268}
6269
6270// ---------------------------------------------------------------------------
6271/// 匿名类体(NodeData::New{anon_raw} 原文)里的标识符全集 + 字符串字面量
6272/// 简单名全集(反射按名查找:MethodHandles.findStatic(X, "putByte", …))。
6273/// 死码分析的引用扫描对二者均不可见——IteratorWildcard 抓获:匿名体内
6274/// 调用的 sideCast/adaptSelf 被删(cannot find symbol)、DomainCombiner
6275/// import 被删、findStatic("putByte") 目标被删(ExceptionInInitializerError)。
6276fn collect_anon_and_string_names(
6277    ast: &JavaAst,
6278    unit: &CompilationUnit,
6279) -> std::collections::HashSet<String> {
6280    let mut out: std::collections::HashSet<String> = Default::default();
6281    let mut bodies: Vec<JavaId> = Vec::new();
6282    for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut bodies));
6283    for root in bodies {
6284        let mut stack = vec![root];
6285        while let Some(n) = stack.pop() {
6286            match ast.data(n) {
6287                NodeData::New { anon_raw: Some(raw), .. } => {
6288                    for id in raw.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6289                        if !id.is_empty() {
6290                            out.insert(id.to_string());
6291                        }
6292                    }
6293                }
6294                NodeData::Literal(Lit::Str(text)) => {
6295                    for id in text.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6296                        if !id.is_empty() {
6297                            out.insert(id.to_string());
6298                        }
6299                    }
6300                }
6301                _ => {}
6302            }
6303            for &c in ast.children(n) {
6304                stack.push(c);
6305            }
6306        }
6307    }
6308    out
6309}
6310
6311// 死私有方法删除(opt-in,Config::remove_dead_methods):
6312// const_method_inline / 解密器还原后残留的 helper 清理。
6313// 安全域:
6314//   - 仅 private(private 不可能被外部调用或被子类覆盖);
6315//   - 全单元零引用:Call 的 callee 名(Member 名/裸名)与 MethodRef 名,
6316//     名字匹配不判签名——匹配只会导致"保留",方向保守;
6317//     (引用扫描是方法命名空间的完整覆盖:字段名与方法名解析域不相交
6318//     (bd.java 字段 b 与方法 b 共存);方法重载撞名但零调用 = 同名
6319//     任何方法都未被调用,private 者可删——旧字段计数守卫曾误留
6320//     bd 的 8 个空方法));
6321//   - 修饰符串含 @(注解方法,可能是框架入口)→ 保留;
6322//   - 构造器不删(单例模式 private ctor 是活的)。
6323// 反射调用无法静态排除 → opt-in 而非默认。
6324// 传递性死代码:迭代到不动点(删一层后重收集引用)。
6325// ---------------------------------------------------------------------------
6326
6327/// **死码分析统一超集扫描**:单元内对「引用扫描不可见域」的全量兜底。
6328/// 覆盖(第 9 轮 dead-code 全审 761 处悬空的 6 类根因):
6329///   - NodeData::Raw 文本(语句级注解 → Raw:guava TypeToken 34 个
6330///     悬空方法主因)
6331///   - New{anon_raw} 匿名类体(局部类调用:grpc 15 个方法)
6332///   - 成员 mods 注解实参(@Method("name") 按名反射 / @MethodSource)
6333///   - 枚举常量原文(guava CaseFormat 等调用在枚举体)
6334///   - ForEach 元素类型(import 悬空 383 例)
6335///   - NewArray 数组类型(import 悬空)
6336///   - 方法级 ty_params 泛型界(jspecify @Nullable 90 例)
6337///   - 字符串字面量(反射按名查找)
6338/// 返回:名字出现集(保守超集——名字出现在任何不可见域即视作引用)
6339fn collect_all_blind_refs(
6340    ast: &JavaAst,
6341    unit: &CompilationUnit,
6342) -> std::collections::HashSet<String> {
6343    let mut out: std::collections::HashSet<String> = Default::default();
6344    let split_into = |text: &str, out: &mut std::collections::HashSet<String>| {
6345        for id in text.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6346            if !id.is_empty() {
6347                out.insert(id.to_string());
6348            }
6349        }
6350    };
6351    // 1) AST 节点层:Raw / anon_raw / 字符串字面量 / ForEach ty / NewArray ty
6352    let mut bodies: Vec<JavaId> = Vec::new();
6353    for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut bodies));
6354    for root in bodies {
6355        let mut stack = vec![root];
6356        while let Some(n) = stack.pop() {
6357            match ast.data(n) {
6358                NodeData::Raw { text } => split_into(text, &mut out),
6359                NodeData::New { anon_raw: Some(raw), .. } => split_into(raw, &mut out),
6360                NodeData::Literal(Lit::Str(text)) => split_into(text, &mut out),
6361                // ForEach 元素类型(类型名在节点数据不在孩子)
6362                NodeData::ForEach { ty, .. } => {
6363                    collect_ty_idents(ty, &mut out);
6364                }
6365                // NewArray 数组创建类型
6366                NodeData::NewArray { ty, .. } => {
6367                    collect_ty_idents(ty, &mut out);
6368                }
6369                // Lambda 显式参数类型((Map.Entry<K, V> entry) -> ——params_raw
6370                // 是载荷字符串孩子不走):auto MoreStreams 抓获——
6371                // `import java.util.Map` 曾因 lambda 参数类型不可见被误删
6372                NodeData::Lambda { params_raw } => {
6373                    split_into(params_raw, &mut out);
6374                }
6375                _ => {}
6376            }
6377            for &c in ast.children(n) {
6378                stack.push(c);
6379            }
6380        }
6381    }
6382    // 1.6) 顶层 raw 区(unit.raws——破损输入的保真残段):其文本中的
6383    // 类型/名字引用对一切句法扫描不可见(BuilderSimpleOnRecord 抓获:
6384    // `<T> {…List<T> l…}` 残段内的 List 曾不可见 → import 误删悬空)
6385    for r in &unit.raws {
6386        split_into(r, &mut out);
6387    }
6388    // 2) 结构层:成员 mods 注解实参 / 枚举常量原文 / 方法级 ty_params /
6389    //    record 组件头
6390    for_each_type(unit, &mut |ty: &TypeDecl| {
6391        collect_mods_idents_all(&ty.mods, &mut out);
6392        for e in &ty.enum_constants {
6393            split_into(e, &mut out);
6394        }
6395        split_into(&ty.ty_params, &mut out);
6396        split_into(&ty.header, &mut out);
6397        for m in &ty.members {
6398            match m {
6399                Member::Method { mods, name, ty_params, params, .. }
6400                | Member::Constructor { mods, name, ty_params, params, .. } => {
6401                    collect_mods_idents_all(mods, &mut out);
6402                    split_into(ty_params, &mut out);
6403                    for p in params {
6404                        collect_mods_idents_all(&p.mods, &mut out);
6405                        collect_ty_idents(&p.ty, &mut out);
6406                    }
6407                    // 裸 @MethodSource(无实参)——JUnit5 约定:测试方法自身
6408                    // 名字即工厂方法名(junit5 BeforeAndAfterSuiteTests /
6409                    // commons-io IOUtilsTest 抓获:工厂误删运行时
6410                    // MethodSource 解析失败)
6411                    if has_bare_methodsource(mods) {
6412                        out.insert(name.clone());
6413                    }
6414                }
6415                Member::Field { mods, declarators, .. } => {
6416                    collect_mods_idents_all(mods, &mut out);
6417                    // 注意:字段**声明名**不进 blind 集(自己删自己永真)
6418                    let _ = declarators;
6419                }
6420                Member::Type(t) => {
6421                    let _ = t; // for_each_type 已递归
6422                }
6423                // @interface 成员整段 Raw(annotation 成员无专用节点):
6424                // 其中的类型/常量/方法引用对一切句法扫描不可见
6425                //(junit5 Timeout 抓获:`TimeUnit unit() default TimeUnit
6426                // .SECONDS;` 曾使 import java.util.concurrent.TimeUnit
6427                // 误删悬空 28 文件;AnnoCaller 复现:Raw 体内调用的
6428                // 私有方法/字段曾误删)
6429                Member::Raw(text) => {
6430                    split_into(text, &mut out);
6431                }
6432                _ => {}
6433            }
6434        }
6435    });
6436    out
6437}
6438
6439/// mods 全文本切分(含注解实参——@Method(METHOD_NAME) 常量引用 /
6440/// @MethodSource("name") 按名反射——dubbo MethodConfigTest 34 个
6441/// 悬空字段主因)。
6442fn collect_mods_idents_all(mods: &str, out: &mut std::collections::HashSet<String>) {
6443    for id in mods.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6444        if !id.is_empty() {
6445            out.insert(id.to_string());
6446        }
6447    }
6448}
6449
6450/// mods 中是否出现**裸** `@MethodSource`(注解名后不接 `(`——简单名或
6451/// 全限定名均可:`@MethodSource` / `@org.junit.jupiter.params.provider
6452/// .MethodSource`)。
6453/// JUnit5 约定:无实参的 @MethodSource 按测试方法自身名字解析工厂——
6454/// 该名字必须按「已引用」处理(具名实参 `@MethodSource("name")` 的
6455/// 名字已由 mods 切分覆盖)。
6456fn has_bare_methodsource(mods: &str) -> bool {
6457    let hay = mods.as_bytes();
6458    const NEEDLE: &[u8] = b"MethodSource";
6459    let mut i = 0usize;
6460    while let Some(p) = mods[i..].find("MethodSource") {
6461        let at = i + p;
6462        let end = at + NEEDLE.len();
6463        // 前驱须为 @ 或 .(排除 MyMethodSource 类内嵌匹配);
6464        // 后继不得是标识符续字符(排除后缀内嵌)
6465        let prev_ok = at == 0
6466            || !(hay[at - 1].is_ascii_alphanumeric()
6467                || hay[at - 1] == b'_'
6468                || hay[at - 1] == b'$');
6469        let next_ident =
6470            end < hay.len() && (hay[end].is_ascii_alphanumeric() || hay[end] == b'_' || hay[end] == b'$');
6471        if prev_ok && !next_ident {
6472            let mut j = end;
6473            while j < hay.len() && hay[j].is_ascii_whitespace() {
6474                j += 1;
6475            }
6476            if j >= hay.len() || hay[j] != b'(' {
6477                return true;
6478            }
6479        }
6480        i = end;
6481    }
6482    false
6483}
6484
6485/// JVM/库按名反射调用的回调方法(序列化机制等)——零句法调用者是
6486/// 正常形态,删除即语义破坏(IteratorWildcard 抓获:writeObject 删除
6487/// 失去串行化同步与权限预检)。
6488const REFLECTIVE_MAGIC_METHODS: &[&str] = &[
6489    "writeObject",
6490    "readObject",
6491    "readObjectNoData",
6492    "writeReplace",
6493    "readResolve",
6494    "validateObject",
6495];
6496
6497fn remove_dead_private_methods(ast: &JavaAst, unit: &mut CompilationUnit) -> usize {
6498    use std::collections::HashSet;
6499    let mut removed = 0usize;
6500    // 匿名类体/字符串字面量中出现的名字(引用扫描不可见域)
6501    let anon_names = collect_anon_and_string_names(ast, unit);
6502    // **超集盲扫**:Raw 文本/枚举体/注解实参/局部类等对句法引用扫描
6503    // 不可见的域(第 9 轮全审 34 个悬空方法的 6 类根因——guava
6504    // TypeToken 的 @SuppressWarnings 局部声明、grpc 局部类体、
6505    // rocketmq record 变量名、guava 枚举常量体调用)
6506    let blind = collect_all_blind_refs(ast, unit);
6507    loop {
6508        // 1) 全单元引用(不可变借用阶段):(名字, 实参个数) 精确到元数
6509        //    (非 varargs 下元数=形参数是过载解析的硬约束——bd.java 的
6510        //    public b(String)/b(String,Class,Class[]) 与 private 空
6511        //    b(Hashtable,MessageDigest) 同名不同元数可分离);
6512        //    MethodRef(this::m)与同名 varargs 方法 → 元数不可判定,
6513        //    名字整体否决。
6514        let mut referenced: HashSet<(String, usize)> = HashSet::new();
6515        let mut veto: HashSet<String> = HashSet::new();
6516        let mut roots: Vec<JavaId> = Vec::new();
6517        for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut roots));
6518        for body in roots {
6519            collect_call_arity_refs(ast, body, &mut referenced, &mut veto);
6520        }
6521        for_each_type(unit, &mut |ty| {
6522            for m in &ty.members {
6523                if let Member::Method { name, params, .. } = m {
6524                    if params.iter().any(|p| p.varargs) {
6525                        veto.insert(name.clone());
6526                    }
6527                }
6528            }
6529        });
6530        // 2) 删除(可变借用阶段)
6531        let mut changed = false;
6532        for_each_type_mut(unit, &mut |ty: &mut TypeDecl| {
6533            let before = ty.members.len();
6534            ty.members.retain(|m| {
6535                if let Member::Method { mods, name, params, .. } = m {
6536                    let is_private = mods.split_whitespace().any(|w| w == "private");
6537                    let annotated = mods.contains('@');
6538                    let keep = !is_private
6539                        || annotated
6540                        // 自身 varargs:元数匹配不可靠 → 保留
6541                        || params.iter().any(|p| p.varargs)
6542                        || veto.contains(name)
6543                        // 序列化等机制按名反射调用(无句法调用者是正常
6544                        // 形态);名字出现在字符串字面量(findStatic/
6545                        // getMethod 按名查找)或匿名类体 → 保守保留
6546                        || REFLECTIVE_MAGIC_METHODS.contains(&name.as_str())
6547                        || anon_names.contains(name)
6548                        || blind.contains(name)
6549                        || referenced.contains(&(name.clone(), params.len()));
6550                    keep
6551                } else {
6552                    true
6553                }
6554            });
6555            if ty.members.len() != before {
6556                removed += before - ty.members.len();
6557                changed = true;
6558            }
6559        });
6560        if !changed {
6561            return removed;
6562        }
6563    }
6564}
6565
6566/// 剥离 callee/MethodRef 名里的前导类型实参(witness 保留把
6567/// `<String>pick` 整体并入 Member 名——第 11 轮攻击抓获:死码裸名
6568/// 精确匹配失明 → 私有方法误删输出不可编译)。名字以 `<` 开头必为
6569/// TA 前缀(标识符/`class`/`new` 等形态不可能以 `<` 开头)。
6570fn strip_ta_prefix(name: &str) -> &str {
6571    if !name.starts_with('<') {
6572        return name;
6573    }
6574    let b = name.as_bytes();
6575    let mut depth = 0i32;
6576    for (i, &c) in b.iter().enumerate() {
6577        match c {
6578            b'<' => depth += 1,
6579            b'>' => {
6580                depth -= 1;
6581                if depth == 0 {
6582                    return &name[i + 1..];
6583                }
6584            }
6585            _ => {}
6586        }
6587    }
6588    name // 不平衡(病态)——原样保守
6589}
6590
6591/// (名字, 实参元数) 引用 + MethodRef/未知元数名字否决。
6592fn collect_call_arity_refs(
6593    ast: &JavaAst,
6594    root: JavaId,
6595    out: &mut std::collections::HashSet<(String, usize)>,
6596    veto: &mut std::collections::HashSet<String>,
6597) {
6598    let mut stack = vec![root];
6599    while let Some(id) = stack.pop() {
6600        match ast.data(id) {
6601            NodeData::Call => {
6602                let ch = ast.children(id).to_vec();
6603                if let Some(&callee) = ch.first() {
6604                    let name = match ast.data(callee) {
6605                        NodeData::Member { name } | NodeData::VarRef { name } => {
6606                            ast.sn(*name).to_string()
6607                        }
6608                        _ => String::new(),
6609                    };
6610                    if !name.is_empty() {
6611                        out.insert((strip_ta_prefix(&name).to_string(), ch.len() - 1));
6612                    }
6613                }
6614            }
6615            NodeData::MethodRef { name } => {
6616                if let Some(short) = ast.sn(*name).rsplit("::").next() {
6617                    veto.insert(strip_ta_prefix(short).to_string());
6618                }
6619            }
6620            _ => {}
6621        }
6622        for &c in ast.children(id) {
6623            stack.push(c);
6624        }
6625    }
6626}
6627
6628/// 递归访问(含嵌套类型),只读。
6629fn for_each_type(unit: &CompilationUnit, f: &mut dyn FnMut(&TypeDecl)) {
6630    for ty in &unit.types {
6631        for_each_type_inner(ty, f);
6632    }
6633}
6634fn for_each_type_inner(ty: &TypeDecl, f: &mut dyn FnMut(&TypeDecl)) {
6635    f(ty);
6636    for m in &ty.members {
6637        if let Member::Type(t) = m {
6638            for_each_type_inner(t, f);
6639        }
6640    }
6641}
6642
6643/// 递归访问(含嵌套类型),可变。
6644fn for_each_type_mut(unit: &mut CompilationUnit, f: &mut dyn FnMut(&mut TypeDecl)) {
6645    for ty in &mut unit.types {
6646        for_each_type_mut_inner(ty, f);
6647    }
6648}
6649fn for_each_type_mut_inner(ty: &mut TypeDecl, f: &mut dyn FnMut(&mut TypeDecl)) {
6650    f(ty);
6651    for m in &mut ty.members {
6652        if let Member::Type(t) = m {
6653            for_each_type_mut_inner(t, f);
6654        }
6655    }
6656}
6657
6658/// 汇集一个类型所有可走子树的根(方法体/初始化块/字段初始化器)。
6659fn collect_member_roots(ty: &TypeDecl, out: &mut Vec<JavaId>) {
6660    for m in &ty.members {
6661        match m {
6662            Member::Method { body: Some(b), .. }
6663            | Member::Constructor { body: Some(b), .. } => out.push(*b),
6664            Member::Initializer { body, .. } => out.push(*body),
6665            Member::Field { declarators, .. } => {
6666                for d in declarators {
6667                    if let Some(init) = d.init {
6668                        out.push(init);
6669                    }
6670                }
6671            }
6672            Member::Type(t) => collect_member_roots(t, out),
6673            _ => {}
6674        }
6675    }
6676}
6677
6678/// 子树内收集所有"被调用"的名字:Call 的 callee(Member 名 / 裸 VarRef 名)
6679/// 与 MethodRef 名(`recv::name`)。名字匹配,不判签名。
6680// ---------------------------------------------------------------------------
6681// 空私有方法 no-op 调用清理 + 死私有字段删除(均 opt-in,--dead-code)
6682// 与 remove_dead_private_methods 同一风险域(反射无法静态排除)。
6683// ---------------------------------------------------------------------------
6684
6685/// 收集同名同元数方法候选(名字 → 元数 → 候选表)。
6686/// 资格(全单元递归):
6687///   - 修饰符串含 @(注解框架入口)→ 名字一票否决;
6688///   - varargs(可吸收任意元数调用)→ 名字一票否决;
6689///   - 候选按 (private && 空体) 标记 noop——调用点做类型适用性消解后
6690///     唯一适用且 noop 才删(bd.java:public c(Class, String) 与
6691///     private c(Hashtable, MessageDigest) 同元数,实参类型消解分离)。
6692fn collect_noop_private_methods(ast: &mut JavaAst, unit: &CompilationUnit) {
6693    use std::collections::{HashMap, HashSet};
6694    ast.noop_private_methods.clear();
6695    let mut by_name: HashMap<String, HashMap<usize, Vec<NoopCandidate>>> = HashMap::new();
6696    let mut veto: HashSet<String> = HashSet::new();
6697    for_each_type(unit, &mut |ty: &TypeDecl| {
6698        for m in &ty.members {
6699            if let Member::Method { mods, name, params, body, throws, .. } = m {
6700                if mods.contains('@') || params.iter().any(|p| p.varargs) {
6701                    veto.insert(name.clone());
6702                    continue;
6703                }
6704                let is_private = mods.split_whitespace().any(|w| w == "private");
6705                let empty = match body {
6706                    Some(b) => ast.kind(*b) == NodeKind::Block && ast.children(*b).is_empty(),
6707                    None => false,
6708                };
6709                // throws 非空:删除调用会使外层 catch 失去「异常可在 try
6710                // 体内抛出」的编译依据(InputAstRegressionManyAlternatives
6711                // 抓获:删 foo1() 后 catch(SQLException) 变编译错误)
6712                let no_throws = throws.is_empty();
6713                let cand = NoopCandidate {
6714                    params: params.iter().map(|p| p.ty.clone()).collect(),
6715                    noop: is_private && empty && no_throws,
6716                };
6717                by_name
6718                    .entry(name.clone())
6719                    .or_default()
6720                    .entry(params.len())
6721                    .or_default()
6722                    .push(cand);
6723            }
6724        }
6725    });
6726    // 超集盲扫否决(Raw 文本/枚举体/注解实参里的方法名引用——
6727    // no-op 删除后这些域的调用会悬空)
6728    let blind = collect_all_blind_refs(ast, unit);
6729    // MethodRef(this::m / A::m):绑定元数不可判定 → 名字否决
6730    //(否则 no-op 调用删除先行,引用悬空)
6731    let mut roots: Vec<JavaId> = Vec::new();
6732    for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut roots));
6733    for root in roots {
6734        let mut stack = vec![root];
6735        while let Some(n) = stack.pop() {
6736            if let NodeData::MethodRef { name } = ast.data(n) {
6737                if let Some(short) = ast.sn(*name).rsplit("::").next() {
6738                    veto.insert(strip_ta_prefix(short).to_string());
6739                }
6740            }
6741            for &c in ast.children(n) {
6742                stack.push(c);
6743            }
6744        }
6745    }
6746    for (name, arities) in by_name {
6747        if veto.contains(&name) || blind.contains(&name) {
6748            continue;
6749        }
6750        ast.noop_private_methods.insert(name, arities);
6751    }
6752}
6753
6754/// 未用单类型 import 清理(--dead-code 收尾):简单名在全单元标识符
6755/// 集合(AST 名字/类型 + 结构字符串含泛型实参)中零出现 → 删。
6756/// on-demand(.*)/ static import 保守保留;误删只会在标识符收集
6757/// 漏形式时发生——方向上宁可多保留。
6758fn remove_unused_imports(ast: &JavaAst, unit: &mut CompilationUnit) -> usize {
6759    use std::collections::HashSet;
6760    let mut idents: HashSet<String> = HashSet::new();
6761    // 1) AST 载荷标识符(名字 + 类型 + Raw 原文)
6762    let mut roots: Vec<JavaId> = Vec::new();
6763    for_each_type(unit, &mut |ty| collect_member_roots(ty, &mut roots));
6764    for root in roots {
6765        let mut stack = vec![root];
6766        while let Some(n) = stack.pop() {
6767            match ast.data(n) {
6768                NodeData::VarDecl { name, ty, .. } => {
6769                    idents.insert(name.clone());
6770                    collect_ty_idents(ty, &mut idents);
6771                }
6772                NodeData::VarRef { name } => {
6773                    // 表达式里的 Foo.class 是单个 VarRef(名字="Foo.class"
6774                    // 整串)——按分隔符切出类型名(v.java List.class 抓获:
6775                    // import 误删)
6776                    let n = ast.sn(*name);
6777                    for id in n.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6778                        if !id.is_empty() {
6779                            idents.insert(id.to_string());
6780                        }
6781                    }
6782                }
6783                NodeData::Member { name } => {
6784                    idents.insert(ast.sn(*name).to_string());
6785                }
6786                NodeData::MethodRef { name } => {
6787                    let n = ast.sn(*name);
6788                    for id in n.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6789                        if !id.is_empty() {
6790                            idents.insert(id.to_string());
6791                        }
6792                    }
6793                }
6794                NodeData::Label { name } => {
6795                    idents.insert(name.clone());
6796                }
6797                NodeData::New { ty, anon_raw, .. } => {
6798                    collect_ty_idents(ty, &mut idents);
6799                    // 匿名类体原文:其中的类型/名字引用对 AST 扫描不可见
6800                    //(IteratorWildcard 抓获:DomainCombiner import 误删)
6801                    if let Some(raw) = anon_raw {
6802                        for id in
6803                            raw.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$')
6804                        {
6805                            if !id.is_empty() {
6806                                idents.insert(id.to_string());
6807                            }
6808                        }
6809                    }
6810                }
6811                NodeData::Cast { ty } => {
6812                    collect_ty_idents(ty, &mut idents);
6813                }
6814                NodeData::InstanceOf { ty, .. } => {
6815                    collect_ty_idents(ty, &mut idents);
6816                }
6817                NodeData::Catch { ty_raw, .. } => {
6818                    // catch (Type name)——异常类型原文
6819                    for id in
6820                        ty_raw.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$')
6821                    {
6822                        if !id.is_empty() {
6823                            idents.insert(id.to_string());
6824                        }
6825                    }
6826                }
6827                NodeData::Raw { text } => {
6828                    for id in text.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6829                        if !id.is_empty() {
6830                            idents.insert(id.to_string());
6831                        }
6832                    }
6833                }
6834                _ => {}
6835            }
6836            for &c in ast.children(n) {
6837                stack.push(c);
6838            }
6839        }
6840    }
6841    // 1.5) 超集盲扫(Raw/枚举体/注解实参/ForEach ty/NewArray ty/
6842    //      方法级 ty_params——657 个悬空 import 的根因。idents 是
6843    //      「使用的名字」超集方向,直接并入)
6844    for name in collect_all_blind_refs(ast, unit) {
6845        idents.insert(name);
6846    }
6847    // 1.6) 顶层 raw 区(unit.raws):残段文本中的类型引用(同上——
6848    // 曾不可见使 List 误删)
6849    for r in &unit.raws {
6850        for id in r.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6851            if !id.is_empty() {
6852                idents.insert(id.to_string());
6853            }
6854        }
6855    }
6856    // 2) 结构字符串(mods/注解原文、泛型参数、extends/implements/throws、
6857    //    方法/类型名——字段/参数/返回类型)
6858    for_each_type(unit, &mut |ty| {
6859        collect_mods_idents(&ty.mods, &mut idents);
6860        idents.insert(ty.name.clone());
6861        for id in ty.ty_params.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6862            if !id.is_empty() {
6863                idents.insert(id.to_string());
6864            }
6865        }
6866        // record 组件头原文 `(MouseEvent e, @Anno Comparator<X> c)`——
6867        // 组件类型对 AST 扫描不可见(InputJavadocMethodScopeAnonInner
6868        // 抓获:MouseEvent import 误删)
6869        for id in ty.header.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6870            if !id.is_empty() {
6871                idents.insert(id.to_string());
6872            }
6873        }
6874        for e in ty.extends.iter().chain(ty.implements.iter()) {
6875            for id in e.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6876                if !id.is_empty() {
6877                    idents.insert(id.to_string());
6878                }
6879            }
6880        }
6881        for m in &ty.members {
6882            match m {
6883                Member::Field { mods, ty, declarators } => {
6884                    collect_mods_idents(mods, &mut idents);
6885                    collect_ty_idents(ty, &mut idents);
6886                    for d in declarators {
6887                        idents.insert(d.name.clone());
6888                    }
6889                }
6890                Member::Method { mods, ret, name, params, throws, .. } => {
6891                    collect_mods_idents(mods, &mut idents);
6892                    collect_ty_idents(ret, &mut idents);
6893                    idents.insert(name.clone());
6894                    for p in params {
6895                        collect_mods_idents(&p.mods, &mut idents);
6896                        collect_ty_idents(&p.ty, &mut idents);
6897                        idents.insert(p.name.clone());
6898                    }
6899                    for t in throws {
6900                        for id in t.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6901                            if !id.is_empty() {
6902                                idents.insert(id.to_string());
6903                            }
6904                        }
6905                    }
6906                }
6907                Member::Constructor { mods, name, params, throws, .. } => {
6908                    collect_mods_idents(mods, &mut idents);
6909                    idents.insert(name.clone());
6910                    for p in params {
6911                        collect_mods_idents(&p.mods, &mut idents);
6912                        collect_ty_idents(&p.ty, &mut idents);
6913                        idents.insert(p.name.clone());
6914                    }
6915                    for t in throws {
6916                        for id in t.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6917                            if !id.is_empty() {
6918                                idents.insert(id.to_string());
6919                            }
6920                        }
6921                    }
6922                }
6923                Member::Type(t) => {
6924                    // 嵌套类型递归(for_each_type 已递归——这里不重复)
6925                    let _ = t;
6926                }
6927                _ => {}
6928            }
6929        }
6930    });
6931    // 3) 保留判定
6932    let before = unit.imports.len();
6933    unit.imports.retain(|imp| {
6934        if imp.contains('*') {
6935            return true; // on-demand 无法证明
6936        }
6937        // static import 的简单名可能是方法名(VarRef 收集覆盖),同规则处理
6938        let path = imp.trim_start_matches("static ").trim();
6939        let simple = path.rsplit('.').next().unwrap_or(path).trim();
6940        if simple.is_empty() || !simple.chars().next().is_some_and(|c| c.is_uppercase()) {
6941            return true; // 非类型形态(方法/常量 static import 简单名小写起)→ 保守保留
6942        }
6943        idents.contains(simple)
6944    });
6945    before - unit.imports.len()
6946}
6947
6948fn collect_ty_idents(t: &JType, out: &mut std::collections::HashSet<String>) {
6949    match t {
6950        JType::Ref(r) => {
6951            for id in r.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6952                if !id.is_empty() {
6953                    out.insert(id.to_string());
6954                }
6955            }
6956        }
6957        JType::Array(inner) => collect_ty_idents(inner, out),
6958        _ => {}
6959    }
6960}
6961
6962fn collect_mods_idents(mods: &str, out: &mut std::collections::HashSet<String>) {
6963    // 注解原文(@SuppressWarnings(...))——含参数里的类名引用
6964    if mods.contains('@') {
6965        for id in mods.split(|c: char| !c.is_alphanumeric() && c != '_' && c != '$') {
6966            if !id.is_empty() {
6967                out.insert(id.to_string());
6968            }
6969        }
6970    }
6971}
6972
6973/// 死私有字段删除:名字在全单元所有体内零值引用(VarRef 非 callee 位置
6974/// + Member 名)→ 删声明符;字段的声明符 init 必须是字面量/字面量数组
6975/// 或缺省(有副作用的 init 删不得——静态初始化顺序是可观察行为)。
6976/// 命名空间隔离:Call 的 callee VarRef 是方法名不是字段名(bd.java 的
6977/// 字段 e 与空方法 e 共存);this.e / A.e 的成员名也计入引用。
6978fn remove_dead_private_fields(ast: &JavaAst, unit: &mut CompilationUnit) -> usize {
6979    use std::collections::HashSet;
6980    let mut removed = 0usize;
6981    // 匿名类体/字符串字面量名字(引用扫描不可见)
6982    let anon_names = collect_anon_and_string_names(ast, unit);
6983    // 超集盲扫(68 个悬空字段根因:mods 注解实参 @Method(METHOD_NAME)、
6984    // 枚举体 .add(SHORT_NAME)、Raw 文本局部类)
6985    let blind = collect_all_blind_refs(ast, unit);
6986    // 1) 全单元字段名引用(值位置)
6987    let mut referenced: HashSet<String> = HashSet::new();
6988    let mut bodies: Vec<JavaId> = Vec::new();
6989    for_each_type(unit, &mut |ty: &TypeDecl| {
6990        for m in &ty.members {
6991            match m {
6992                Member::Method { body: Some(b), .. }
6993                | Member::Constructor { body: Some(b), .. }
6994                | Member::Initializer { body: b, .. } => bodies.push(*b),
6995                Member::Field { declarators, .. } => {
6996                    for d in declarators {
6997                        if let Some(init) = d.init {
6998                            bodies.push(init);
6999                        }
7000                    }
7001                }
7002                _ => {}
7003            }
7004        }
7005    });
7006    for root in bodies {
7007        // 栈携带 (节点, 是否为 Call 的 callee——方法命名空间,跳过)
7008        let mut stack: Vec<(JavaId, bool)> = vec![(root, false)];
7009        while let Some((n, is_callee)) = stack.pop() {
7010            match ast.kind(n) {
7011                NodeKind::VarRef if !is_callee => {
7012                    if let Some(nm) = ast.var_name(n) {
7013                        referenced.insert(nm.to_string());
7014                    }
7015                }
7016                NodeKind::Member => {
7017                    if let NodeData::Member { name: sym } = ast.data(n) {
7018                        referenced.insert(ast.sn(*sym).to_string());
7019                    }
7020                }
7021                _ => {}
7022            }
7023            if ast.kind(n) == NodeKind::Call {
7024                let ch = ast.children(n).to_vec();
7025                if let Some(&first) = ch.first() {
7026                    stack.push((first, true));
7027                }
7028                for &c in &ch[1..] {
7029                    stack.push((c, false));
7030                }
7031                continue;
7032            }
7033            for &c in ast.children(n) {
7034                stack.push((c, false));
7035            }
7036        }
7037    }
7038    // 2) 删除(可变借用阶段;嵌套类型递归):
7039    //    先逐成员清理声明符(字面量 init 或无 init 且零引用),再删空成员。
7040    //    serialVersionUID / serialPersistentFields 是 JVM 序列化按名反射
7041    //    读取的契约字段——「零源码引用」判据不适用(a9/a_ 抓获:删除后
7042    //    SUID 退化为结构计算值,旧流反序列化 InvalidClassException)
7043    for_each_type_mut(unit, &mut |ty: &mut TypeDecl| {
7044        for m in ty.members.iter_mut() {
7045            if let Member::Field { mods, declarators, .. } = m {
7046                let is_private = mods.split_whitespace().any(|w| w == "private");
7047                let annotated = mods.contains('@');
7048                if !is_private || annotated {
7049                    continue;
7050                }
7051                let dead: Vec<bool> = declarators
7052                    .iter()
7053                    .map(|d| {
7054                        let reflective =
7055                            d.name == "serialVersionUID" || d.name == "serialPersistentFields";
7056                        let init_ok =
7057                            d.init.map(|i| is_const_init(ast, i)).unwrap_or(true);
7058                        !reflective
7059                            && init_ok
7060                            && !referenced.contains(&d.name)
7061                            && !anon_names.contains(&d.name)
7062                            && !blind.contains(&d.name)
7063                    })
7064                    .collect();
7065                let before = declarators.len();
7066                let mut idx = 0usize;
7067                declarators.retain(|_| {
7068                    let keep = !dead[idx];
7069                    idx += 1;
7070                    keep
7071                });
7072                removed += before - declarators.len();
7073            }
7074        }
7075        let before = ty.members.len();
7076        ty.members
7077            .retain(|m| !matches!(m, Member::Field { declarators, .. } if declarators.is_empty()));
7078        removed += before - ty.members.len();
7079    });
7080    removed
7081}
7082
7083fn simplify_type(ast: &mut JavaAst, ty: &mut TypeDecl, cfg: &Config, total: &mut Report) {
7084    for m in &mut ty.members {
7085        simplify_member(ast, m, cfg, total);
7086    }
7087}
7088
7089fn simplify_member(ast: &mut JavaAst, m: &mut Member, cfg: &Config, total: &mut Report) {
7090    // 方法参数进入作用域基表,供类型解析使用
7091    let params: Vec<(String, JType)> = match m {
7092        Member::Method { params, .. } | Member::Constructor { params, .. } => params
7093            .iter()
7094            .map(|p| (p.name.clone(), p.ty.clone()))
7095            .collect(),
7096        _ => Vec::new(),
7097    };
7098    ast.set_param_scope(&params);
7099    match m {
7100        Member::Method { body: Some(b), .. }
7101        | Member::Constructor { body: Some(b), .. } => {
7102            let r = simplify(ast, *b, cfg);
7103            merge_report(total, r);
7104        }
7105        Member::Initializer { body, .. } => {
7106            let r = simplify(ast, *body, cfg);
7107            merge_report(total, r);
7108        }
7109        Member::Field { declarators, .. } => {
7110            for d in declarators.iter_mut() {
7111                if let Some(init) = d.init {
7112                    let r = simplify(ast, init, cfg);
7113                    merge_report(total, r);
7114                    // 根折叠:字段 init 节点就是 walk 根——Replace 无父
7115                    // 槽可写(引擎既有边界),由本侧拿到新根回写声明
7116                    //(static int x=1+2 曾永不折——本轮修复)
7117                    let rules = default_java_rules();
7118                    let mut cur: JavaId = init;
7119                    for _ in 0..8 {
7120                        match cure_engine::fold_root(ast, cur, &rules, cfg) {
7121                            Some((new_root, name)) => {
7122                                d.init = Some(new_root);
7123                                cur = new_root;
7124                                *total.by_rule.entry(name).or_insert(0) += 1;
7125                                total.edits += 1;
7126                                // 新根内部可能再折
7127                                let r = simplify(ast, new_root, cfg);
7128                                merge_report(total, r);
7129                            }
7130                            None => break,
7131                        }
7132                    }
7133                }
7134            }
7135        }
7136        Member::Type(t) => simplify_type(ast, t, cfg, total),
7137        Member::Raw(_) => {}
7138        _ => {}
7139    }
7140}
7141
7142fn merge_report(total: &mut Report, r: Report) {
7143    total.edits += r.edits;
7144    total.iterations += r.iterations;
7145    for (k, v) in r.by_rule {
7146        *total.by_rule.entry(k).or_insert(0) += v;
7147    }
7148}
7149
7150/// 默认配置。
7151pub fn default_config() -> Config {
7152    Config::default()
7153}