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