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