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