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