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