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