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