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