cure-java-ast 0.4.2

Java AST (arena + NodeId + builder) with cure Lang implementation
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
//! # cure-java-ast
//!
//! Java AST(arena + NodeId)+ 编译单元/成员结构 + builder + [`cure_engine::Lang`] 实现。
//!
//! 覆盖反编译器产出的常见 Java:完整语句集(含 try/catch、switch、for-each、
//! lambda 等)、完整表达式集、类/接口/枚举成员。签名里的泛型/注解以**原文**字符串
//! 保留(保真打印),方法体内才是可简化的 arena 节点。
//!
//! 零外部依赖(仅依赖 cure-engine),可单独作为"Java AST + 语义查询"基础库引用。
//!
//! arena 不变量:children 永远先于父节点入 arena(index 递增)。

use std::collections::{HashMap, HashSet};

use cure_engine::analysis::{subtree_metrics, TreeMetrics};
use cure_engine::lang::{ReassocOutcome, StmtKill};
use cure_engine::Lang;
pub use cure_tree::ChildList;
use cure_tree::{EventStore, NameTable};

// 本 crate 自用 + 供下游 crate(如 cure-java-print/parser)免依赖引擎直接使用
pub use cure_engine::kind::{BinOp, LitRef, NodeKind, UnOp};
pub use cure_engine::Effect;

// ---------------------------------------------------------------------------
// 基础类型
// ---------------------------------------------------------------------------

/// arena 句柄。
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct JavaId(pub u32);
impl Default for JavaId {
    fn default() -> Self {
        JavaId(u32::MAX)
    }
}

/// Java 类型(第一阶段的最小集合;`Ref` 存类型名字符串,泛型参数并入字符串)。
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum JType {
    Bool,
    Byte,
    Short,
    Int,
    Long,
    Char,
    Float,
    Double,
    Void,
    /// 引用类型,存点分名或简单名(可含泛型参数原文,如 `Map<String, ?>`)。
    Ref(String),
    Array(Box<JType>),
    /// var / 推断类型 / 未知。
    Var,
}

impl JType {
    pub fn is_ref(&self) -> bool {
        matches!(self, JType::Ref(_) | JType::Array(_))
    }
    pub fn is_integral(&self) -> bool {
        matches!(
            self,
            JType::Byte | JType::Short | JType::Int | JType::Long | JType::Char
        )
    }
    pub fn is_bool(&self) -> bool {
        matches!(self, JType::Bool)
    }
}

/// 数值字面量的解析值(用于语义查询;原文见 [`Lit::NumRaw`])。
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum NumVal {
    Int(i64),
    Long(i64),
    Float(f64),
    Double(f64),
}

/// 字面量。
#[derive(Clone, PartialEq, Debug)]
pub enum Lit {
    Bool(bool),
    Int(i64),
    Long(i64),
    Float(f64),
    Double(f64),
    Char(char),
    Str(String),
    /// 文本块(`"""..."""`),存去引号后的原文。
    TextBlock(String),
    /// 带原文的数值字面量:保留 0x1F / 1.5e2f / 1_000_000L 等原文形式。
    NumRaw { text: String, val: NumVal },
    Null,
}

/// 节点负载:children 统一放在 [`Node::children`],这里只放非节点数据。
#[derive(Clone, PartialEq, Debug)]
pub enum NodeData {
    // ---- 语句 ----
    Block,
    /// 合成分组(无花括号、**无作用域**):多声明符等"一句多语句"的承载。
    /// 语句列表处在解析期就地展开;若存活到打印,按同缩进无括号输出。
    /// kind() 映射为 Block(引擎规则透明),但语义上不引入词法作用域。
    Group,
    Empty,
    /// `yield expr;`(switch 表达式产出)。孩子 [expr]。
    Yield,

    ExprStmt,
    VarDecl { name: String, ty: JType, final_mod: bool },
    Assign { op: Option<BinOp> },
    If,
    While,
    DoWhile,
    For { inits: u8, has_cond: bool, steps: u8 },
    ForEach { name: String, ty: JType },
    Return,
    Break { label: Option<String> },
    Continue { label: Option<String> },
    Throw,
    /// children: `[resource…, try_block, catch…, (finally)?]`
    Try,
    /// `catch (ty_raw name)`,children: `[block]`
    Catch { ty_raw: String, name: String },
    /// children: `[lock, block]`
    Synchronized,
    /// children: `[subject, case…]`
    Switch,
    /// children: `[label…, stmts…]`(前 `labels` 个是 case 标签表达式)
    Case { labels: u16, is_default: bool, arrow: bool },
    /// children: `[stmt]`
    Label { name: String },
    /// children: `[cond, (msg)?]`
    Assert,
    /// 不可解析的原文区域:原样保留,简化规则不触碰。
    Raw { text: String },

    // ---- 表达式 ----
    Binary { op: BinOp },
    Unary { op: UnOp },
    /// children: `[callee, arg…]`
    Call,
    /// children: `[object]`;`name` 为成员名。
    Member { name: Sym },
    /// children: `[array, index]`
    Index,
    Cast { ty: JType },
    Paren,
    Ternary,
    /// `new T(args…)`;匿名类体以原文附加。
    New { ty: JType, anon_raw: Option<String> },
    /// `new T[sz…][]… [init]`;ty 为元素类型,dims=总维数,sized=带尺寸维数;
    /// children: `[size…, (ArrayLit)?]`
    NewArray { ty: JType, dims: u16, sized: u16 },
    ArrayLit,
    VarRef { name: Sym },
    Literal(Lit),
    This,
    /// `super`(super.foo() / super(args) 的 callee)
    Super,
    /// children: `[expr]`;`x instanceof T bind`
    InstanceOf { ty: JType, bind: Option<String> },
    /// children: `[body]`(body 为 Block 或表达式)
    Lambda { params_raw: String },
    /// children: `[receiver]`;`recv::name`(name 含 `new`)
    MethodRef { name: Sym },
}

#[derive(Clone, PartialEq, Debug)]
pub struct Node {
    pub data: NodeData,
    pub children: ChildList<JavaId>,
}

/// 符号(intern 名字 id)。VarRef/Member/MethodRef 的名字字段——
/// 87% 重复率(实测 jdk-sources:4754 个名字节点仅 669 个唯一),
/// String→Sym 后:构造去重、比较 u32、clone 零分配(copy_subtree/
/// clone_node 全线受益)。
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
pub struct Sym(pub u32);

/// Lit 值级相等(payload_equal 用;TextBlock 无值语义 → false)。
fn lit_value_eq(x: &Lit, y: &Lit) -> bool {
    fn r(l: &Lit) -> Option<cure_engine::kind::LitRef<'_>> {
        Some(match l {
            Lit::Bool(b) => LitRef::Bool(*b),
            Lit::Int(v) => LitRef::Int(*v),
            Lit::Long(v) => LitRef::Long(*v),
            Lit::Float(v) => LitRef::Float(*v),
            Lit::Double(v) => LitRef::Double(*v),
            Lit::Char(c) => LitRef::Char(*c),
            Lit::Str(s) => LitRef::Str(s.as_str()),
            Lit::NumRaw { val, .. } => match val {
                NumVal::Int(v) => LitRef::Int(*v),
                NumVal::Long(v) => LitRef::Long(*v),
                NumVal::Float(v) => LitRef::Float(*v),
                NumVal::Double(v) => LitRef::Double(*v),
            },
            Lit::Null => LitRef::Null,
            Lit::TextBlock(_) => return None,
        })
    }
    // ±0.0 位模式区分(IEEE == 视相等但 1/x 符号不同——ES
    // StripeStatsHarvesterTests 抓获:b ? 0.0 : -0.0 曾折成 0.0)
    match (r(x), r(y)) {
        (Some(rx), Some(ry)) => {
            if let (LitRef::Double(a), LitRef::Double(b)) = (rx, ry) {
                return a == b && a.is_sign_negative() == b.is_sign_negative();
            }
            if let (LitRef::Float(a), LitRef::Float(b)) = (rx, ry) {
                return a == b && a.is_sign_negative() == b.is_sign_negative();
            }
            rx == ry
        }
        _ => false,
    }
}

/// 同名同元数方法候选(no-op 调用消解用)。
#[derive(Clone, Debug)]
pub struct NoopCandidate {
    /// 形参类型表。
    pub params: Vec<JType>,
    /// private + 空体(no-op)。
    pub noop: bool,
}


// ---------------------------------------------------------------------------
// 外部事实(front-end facts)——cure-engine Lang 事实查询面的 Java 载体。
//
// 持有 IR/类型/数据流信息的调用方(反编译前端)对源码可见变量与被调
// 成员的权威陈述。语言无关的词汇语义见 cure_engine::lang 的 trait 文档;
// 本结构只是键的承载(局部名 → 变量事实;成员名 → 调用副作用上限)。
// 生命周期 = 编译单元:换单元必须 clear_facts()(simplify_unit 门面
// 已自动管理)。零依赖 Rust 结构(序列化形态留给后续 feature 门控)。
// ---------------------------------------------------------------------------

/// 单个局部变量的事实集(全部缺省 false = 未知/不成立)。
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct VarFacts {
    /// 源码外无隐藏写,可见写恰为一次(单一定义)。
    pub single_def: bool,
    /// 读点恰为一次(前端权威计数)。
    pub single_use: bool,
    /// 静态类型为原始 boolean(寄存器复用 demux 的真值)。
    pub bool_valued: bool,
}

/// 编译单元级事实集:局部名(前端发射的名字与源码文本一致——名字是
/// 文本往返的稳定键)→ 变量事实;被调成员名 → 副作用上限。
#[derive(Debug, Clone, Default)]
pub struct JavaFacts {
    pub vars: HashMap<String, VarFacts>,
    pub call_effects: HashMap<String, std::collections::HashSet<cure_engine::Effect>>,
}

impl JavaFacts {
    pub fn var(&self, name: &str) -> VarFacts {
        self.vars.get(name).copied().unwrap_or_default()
    }
}

/// Per-statement syntactic name facts used by propagation rules: VarRef
/// multiplicity plus whether a lambda in the subtree captures the name.
/// Built once per statement alongside the region-event index and
/// invalidated by the same `invalidate_effect` ancestor walk.
#[derive(Clone, Copy, Debug)]
struct StmtFact {
    key: u32,
    refs: u32,
    lambda_cap: bool,
    /// VarDecl/ForEach/Catch declares this name (region-event Shadow).
    shadow: bool,
}

/// Per-statement kill event (`collect_stmt_kill_events`). `kind`:
/// 1 = Read, 2 = Shadow, 3 = DomWrite.
#[derive(Clone, Copy, Debug)]
struct StmtKillFact {
    key: u32,
    kind: u8,
    assign: JavaId,
}


#[derive(Clone, Copy, Debug)]
struct StmtOpq {
    opaque: bool,
}

#[derive(Default, Debug)]
pub struct JavaAst {
    pub(crate) nodes: Vec<Node>,
    /// 外部事实(前端注入;编译单元生命周期)。None = 纯源码保守模式。
    pub facts: Option<JavaFacts>,
    /// prepare() 重建:JavaId → 聚合效果。
    /// 槽位 = arena 下标(稠密 u32);None = 未算/失效。Vec 索引替代哈希。
    effect_cache: Vec<Option<Effect>>,
    /// prepare() 重建:VarRef 节点 → 声明类型(作用域解析)。
    /// 槽位 = arena 下标;None = 未解析。Vec 索引替代哈希。
    var_types: Vec<Option<JType>>,
    /// 方法参数类型(由 simplify 门面按方法设置,作为根作用域)。
    param_scope: Vec<(String, JType)>,
    /// 类级常量字段(static final 且字面量/字面量数组初始化、无写、无同名局部)
    /// → 初始化节点。由 simplify_unit 填充(跨方法解密的字符串表)。
    pub const_fields: HashMap<String, JavaId>,
    /// 可安全做 VarRef → 字面量传播的常量字段名(标量字面量且声明类型
    /// 与字面量种类精确匹配——过载解析/常量池身份不受影响)。
    /// 数组常量不进(只走下标折叠,整表引用不复制)。
    pub const_scalars: HashSet<String>,
    /// 空私有方法(no-op 调用清理用):名字 → 元数 → 同名同元数全部
    /// 候选(参数类型表 + 是否 private 空体)。调用点做类型适用性消解
    ///(唯一适用者且为 private 空体 → no-op)。同名任意方法含 varargs →
    /// 该名整体放弃(varargs 可吸收任意元数)。由 simplify_unit 填充
    ///(--dead-code 门控)。
    pub noop_private_methods: HashMap<String, HashMap<usize, Vec<NoopCandidate>>>,
    /// 字段声明类型表(名字 → JType;同名二见移除)——调用点实参静态
    /// 类型解析用(VarRef 解析不到局部时查字段)。collect_unit_consts 填充。
    pub field_types: HashMap<String, JType>,
    /// 本单元声明了名为 String/StringBuilder 的类型(遮蔽 java.lang——
    /// new String("x") 折叠/字符串链折叠在该单元全部拒绝。探针 T4a/b
    /// 抓获:`class String{…}` 同文件时 new String 折叠产出不可编译或
    /// 静默错值)。collect_unit_consts 每轮重算。
    pub unit_shadows_lang: bool,
    /// 已知为**泛型**的类型简单名(单元内带 ty_params 的类型 + 常见
    /// JDK 泛型库类)。raw 引用(无类型实参)这些名字时,接收位传播
    /// 改变成员解析(raw 擦除 vs 泛型形参——CAP#1 编译错)。
    pub raw_generic_names: std::collections::HashSet<String>,
    /// final 字段名集合(同名二见移除——保守)。static_exec 截断守卫
    /// 用:前缀字段写被 rest 再赋值时,仅 final 才放弃(非 final 的
    /// 材料化写是死写,语义恒等、可编译)。
    pub final_fields: HashSet<String>,
    /// 可内联的单 return 方法:名字 → (参数名表, 返回表达式节点, 声明
    /// 该方法的类的简单名)。owner 供 const_method_inline 的归属守卫:
    /// 限定调用的接收者类名必须与之一致——名字匹配不判归属会把 JDK 的
    /// Integer.valueOf 当本类同名方法内联。由 simplify_unit 填充(解密
    /// helper:d(0) → 方法体)。
    pub inline_methods: HashMap<String, (Vec<String>, JavaId, String)>,
    /// String 引用身份比较缓存:(root, 结果)。prepare() 清空(树已变),
    /// 首次查询时计算——供 new String(lit) 等折叠守卫复用(每轮至多一次全扫)。
    string_identity: std::cell::Cell<Option<(JavaId, bool)>>,
    /// **单元级** String 身份比较存在标志(collect_unit_consts 每轮重算)。
    /// new String(lit) 解包为池化字面量会翻转任何 == 观察点——字段 init
    /// 解包的观察点在**其他方法**里(InputNoWhitespaceAfterWithEmoji 抓获
    /// 合成证实:a == "hello" false→true),单根扫描看不见 → 单元级守卫。
    pub unit_string_identity: bool,
    /// 名字 intern 表 + 节点名字键(cure-tree `NameTable`;本语言
    /// `Lang::NameKey = u32` intern id)。单元级持久、只增;prepare
    /// 增量扩展新节点,既有节点键与其名字恒同。
    names: NameTable,
    /// 区域事件索引(cure-tree `EventStore`):B3 惰性重建 + 编辑失效。
    /// 供引擎 scan_region 快路径——收集器在 cure-tree,只依赖 Lang
    /// 钩子,遍历序与引擎原递归严格一致。
    events: EventStore<JavaId, u32>,
    /// 语句级名字事实(稠密槽 = 语句根 arena 下标;None = 未建/失效)。
    stmt_facts: Vec<Option<Box<[StmtFact]>>>,
    /// 语句级 kill 事件(同一槽位与失效机制)。
    stmt_kills: Vec<Option<Box<[StmtKillFact]>>>,
    /// 语句是否含不透明区域(与其余索引同一失效机制)。
    stmt_opq: Vec<Option<StmtOpq>>,
    /// 上次 prepare 的根。B3 早退(events_clean)只对**树全局**缓存
    /// (事件索引/效果表)合法;var_types 是 **root 相关**的作用域分析
    /// ——根变化时必须重建,否则 is_local_var 全 false、传播规则停摆
    /// (deobfuscate 差分抓获:`t = bump(2)` 不再内联)。
    prepared_root: Option<JavaId>,
}

// ---------------------------------------------------------------------------
// 编译单元 / 成员(签名层:签名保真、方法体进 arena)
// ---------------------------------------------------------------------------

/// 方法/构造器参数。
#[derive(Clone, PartialEq, Debug)]
pub struct Param {
    /// `final` / 注解等原文(可为空串)。
    pub mods: String,
    pub ty: JType,
    /// `int... args`
    pub varargs: bool,
    pub name: String,
}

/// 字段声明中的一个声明符(`int a = 1, b[];` 里的 a、b)。
#[derive(Clone, PartialEq, Debug)]
pub struct Declarator {
    pub name: String,
    /// C 风格后缀维度 `int a[][]` 中名字后面的 `[]` 数。
    pub extra_dims: u16,
    /// 初始化表达式(arena 节点)。
    pub init: Option<JavaId>,
}

/// 类成员。
#[derive(Clone, PartialEq, Debug)]
pub enum Member {
    Field { mods: String, ty: JType, declarators: Vec<Declarator> },
    Method {
        mods: String,
        /// 泛型参数原文(含尖括号,可为空串)。
        ty_params: String,
        ret: JType,
        name: String,
        params: Vec<Param>,
        throws: Vec<String>,
        /// 方法体 Block;None 表示 abstract/native/接口声明。可能是 Raw 节点。
        body: Option<JavaId>,
    },
    Constructor {
        mods: String,
        ty_params: String,
        name: String,
        params: Vec<Param>,
        throws: Vec<String>,
        body: Option<JavaId>,
        /// record 紧凑构造器:`Name { … }`(无参数列表,签名继承自 record 头)
        compact: bool,
    },
    /// 实例/静态初始化块。
    Initializer { is_static: bool, body: JavaId },
    /// 嵌套类型。
    Type(Box<TypeDecl>),
    /// 不可解析成员(原文保真)。
    Raw(String),
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum TypeKind {
    Class,
    Interface,
    Enum,
    Record,
    Annotation,
}

/// 类型声明。
#[derive(Clone, PartialEq, Debug)]
pub struct TypeDecl {
    pub kind: TypeKind,
    /// 修饰符 + 注解原文。
    pub mods: String,
    pub name: String,
    /// 泛型参数原文(含尖括号,可为空串);record 的头部参数原文存在 header。
    pub ty_params: String,
    /// record 组件头原文(`(int a, String b)`),非 record 为空串。
    pub header: String,
    pub extends: Vec<String>,
    pub implements: Vec<String>,
    /// sealed 类型的 permits 子句原文列表(打印 `permits A, B`)。
    pub permits: Vec<String>,
    /// 枚举常量原文(解析失败时兜底保真)。
    pub enum_constants: Vec<String>,
    pub members: Vec<Member>,
}

/// 编译单元。
#[derive(Clone, PartialEq, Debug, Default)]
pub struct CompilationUnit {
    /// package 名(不含 `package`/`;`)。
    pub package: Option<String>,
    /// package 声明前的包级注解原文(package-info.java 的 @NullMarked /
    /// @Deprecated——149 文件曾静默丢弃,RUNTIME 保留期改变整包契约)。
    pub package_annotations: String,
    /// 文件头注释块(首条声明之前):license header、`//noinspection` 等。
    /// 打印器原样放在 package/import 之前。
    pub header_comments: String,
    /// import 项原文(如 `java.util.List` / `static x.y.*`)。
    pub imports: Vec<String>,
    pub types: Vec<TypeDecl>,
    /// 顶层不可解析区域原文(按出现顺序保真)。
    pub raws: Vec<String>,
}

/// 解析错误(位置为 1-based 行/列)。
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ParseError {
    pub line: usize,
    pub col: usize,
    pub message: String,
}

// ---------------------------------------------------------------------------
// JavaAst 主体
// ---------------------------------------------------------------------------

impl JavaAst {
    pub fn new() -> Self {
        Self::default()
    }

    /// 以 node 的负载 + 指定 children 构造新节点(深拷贝/替换用)。
    pub fn clone_node(&mut self, id: JavaId, children: Vec<JavaId>) -> JavaId {
        let data = self.nodes[id.0 as usize].data.clone();
        self.push(data, children)
    }

    fn push(&mut self, data: NodeData, children: Vec<JavaId>) -> JavaId {
        let id = JavaId(self.nodes.len() as u32);
        self.nodes.push(Node { data, children: ChildList::from_vec(children) });
        id
    }

    pub fn node(&self, id: JavaId) -> &Node {
        &self.nodes[id.0 as usize]
    }
    pub fn data(&self, id: JavaId) -> &NodeData {
        &self.node(id).data
    }

    /// 固有方法(与 Lang trait 同名;供不依赖引擎的下游使用,如 printer)。
    pub fn kind(&self, id: JavaId) -> NodeKind {
        match &self.nodes[id.0 as usize].data {
            NodeData::Block | NodeData::Group => NodeKind::Block,
            NodeData::Empty => NodeKind::Empty,
            NodeData::Yield => NodeKind::Yield,
            NodeData::ExprStmt => NodeKind::ExprStmt,
            NodeData::VarDecl { .. } => NodeKind::VarDecl,
            NodeData::Assign { .. } => NodeKind::Assign,
            NodeData::If => NodeKind::If,
            NodeData::While => NodeKind::While,
            NodeData::DoWhile => NodeKind::DoWhile,
            NodeData::For { .. } => NodeKind::For,
            NodeData::ForEach { .. } => NodeKind::ForEach,
            NodeData::Return => NodeKind::Return,
            NodeData::Break { .. } => NodeKind::Break,
            NodeData::Continue { .. } => NodeKind::Continue,
            NodeData::Throw => NodeKind::Throw,
            NodeData::Try => NodeKind::Try,
            NodeData::Catch { .. } => NodeKind::Catch,
            NodeData::Synchronized => NodeKind::Synchronized,
            NodeData::Switch => NodeKind::Switch,
            NodeData::Case { .. } => NodeKind::Case,
            NodeData::Label { .. } => NodeKind::Label,
            NodeData::Assert => NodeKind::Assert,
            NodeData::Raw { .. } => NodeKind::Raw,
            NodeData::Binary { .. } => NodeKind::Binary,
            NodeData::Unary { .. } => NodeKind::Unary,
            NodeData::Call => NodeKind::Call,
            NodeData::Member { .. } => NodeKind::Member,
            NodeData::Index => NodeKind::Index,
            NodeData::Cast { .. } => NodeKind::Cast,
            NodeData::Paren => NodeKind::Paren,
            NodeData::Ternary => NodeKind::Ternary,
            NodeData::New { .. } => NodeKind::New,
            NodeData::NewArray { .. } => NodeKind::NewArray,
            NodeData::ArrayLit => NodeKind::ArrayLit,
            NodeData::VarRef { .. } => NodeKind::VarRef,
            NodeData::Literal(_) => NodeKind::Literal,
            NodeData::This => NodeKind::This,
            NodeData::Super => NodeKind::This,
            NodeData::InstanceOf { .. } => NodeKind::InstanceOf,
            NodeData::Lambda { .. } => NodeKind::Lambda,
            NodeData::MethodRef { .. } => NodeKind::MethodRef,
        }
    }
    /// arena 节点总数(诊断/深度测量用)。
    pub fn node_count(&self) -> usize {
        self.nodes.len()
    }

    pub fn children(&self, id: JavaId) -> &[JavaId] {
        self.nodes[id.0 as usize].children.as_slice()
    }

    // ---- builder:语句 ----

    pub fn block(&mut self, stmts: Vec<JavaId>) -> JavaId {
        self.push(NodeData::Block, stmts)
    }
    pub fn empty(&mut self) -> JavaId {
        self.push(NodeData::Empty, vec![])
    }
    pub fn expr_stmt(&mut self, e: JavaId) -> JavaId {
        self.push(NodeData::ExprStmt, vec![e])
    }
    pub fn var_decl(&mut self, name: &str, ty: JType, init: Option<JavaId>) -> JavaId {
        self.var_decl_final(name, ty, init, false)
    }
    /// final 局部声明(JLS 4.12.4:case 标签常量的必要条件——
    /// final 局部删除曾致 `case ARRAY_BOUND:` 失去常量性不可编译,
    /// LargeFile.java 抓获)。
    pub fn var_decl_final(
        &mut self,
        name: &str,
        ty: JType,
        init: Option<JavaId>,
        final_mod: bool,
    ) -> JavaId {
        self.push(
            NodeData::VarDecl {
                name: name.into(),
                ty,
                final_mod,
            },
            init.into_iter().collect(),
        )
    }
    pub fn assign(&mut self, target: JavaId, value: JavaId) -> JavaId {
        self.push(NodeData::Assign { op: None }, vec![target, value])
    }
    pub fn assign_op(&mut self, op: BinOp, target: JavaId, value: JavaId) -> JavaId {
        self.push(NodeData::Assign { op: Some(op) }, vec![target, value])
    }
    pub fn if_(&mut self, cond: JavaId, then: JavaId, els: Option<JavaId>) -> JavaId {
        let mut ch = vec![cond, then];
        ch.extend(els);
        self.push(NodeData::If, ch)
    }
    pub fn while_(&mut self, cond: JavaId, body: JavaId) -> JavaId {
        self.push(NodeData::While, vec![cond, body])
    }
    pub fn do_while(&mut self, body: JavaId, cond: JavaId) -> JavaId {
        self.push(NodeData::DoWhile, vec![body, cond])
    }
    pub fn for_(
        &mut self,
        inits: Vec<JavaId>,
        cond: Option<JavaId>,
        steps: Vec<JavaId>,
        body: JavaId,
    ) -> JavaId {
        let ni = inits.len();
        let ns = steps.len();
        let mut ch = inits;
        let has_cond = cond.is_some();
        ch.extend(cond);
        ch.extend(steps);
        ch.push(body);
        self.push(
            NodeData::For {
                inits: ni as u8,
                has_cond,
                steps: ns as u8,
            },
            ch,
        )
    }
    pub fn for_each(&mut self, name: &str, ty: JType, iterable: JavaId, body: JavaId) -> JavaId {
        self.push(
            NodeData::ForEach {
                name: name.into(),
                ty,
            },
            vec![iterable, body],
        )
    }
    pub fn yield_(&mut self, expr: JavaId) -> JavaId {
        self.push(NodeData::Yield, vec![expr])
    }
    pub fn ret(&mut self, value: Option<JavaId>) -> JavaId {
        self.push(NodeData::Return, value.into_iter().collect())
    }
    pub fn break_(&mut self, label: Option<&str>) -> JavaId {
        self.push(
            NodeData::Break {
                label: label.map(Into::into),
            },
            vec![],
        )
    }
    pub fn continue_(&mut self, label: Option<&str>) -> JavaId {
        self.push(
            NodeData::Continue {
                label: label.map(Into::into),
            },
            vec![],
        )
    }
    pub fn throw(&mut self, e: JavaId) -> JavaId {
        self.push(NodeData::Throw, vec![e])
    }
    pub fn try_(
        &mut self,
        resources: Vec<JavaId>,
        try_block: JavaId,
        catches: Vec<JavaId>,
        finally: Option<JavaId>,
    ) -> JavaId {
        let mut ch = resources;
        ch.push(try_block);
        ch.extend(catches);
        ch.extend(finally);
        self.push(NodeData::Try, ch)
    }
    pub fn catch_(&mut self, ty_raw: &str, name: &str, block: JavaId) -> JavaId {
        self.push(
            NodeData::Catch {
                ty_raw: ty_raw.into(),
                name: name.into(),
            },
            vec![block],
        )
    }
    pub fn synchronized(&mut self, lock: JavaId, block: JavaId) -> JavaId {
        self.push(NodeData::Synchronized, vec![lock, block])
    }
    pub fn switch_(&mut self, subject: JavaId, cases: Vec<JavaId>) -> JavaId {
        let mut ch = vec![subject];
        ch.extend(cases);
        self.push(NodeData::Switch, ch)
    }
    pub fn case_(
        &mut self,
        labels: Vec<JavaId>,
        is_default: bool,
        arrow: bool,
        stmts: Vec<JavaId>,
    ) -> JavaId {
        let n = labels.len() as u16;
        let mut ch = labels;
        ch.extend(stmts);
        self.push(
            NodeData::Case {
                labels: n,
                is_default,
                arrow,
            },
            ch,
        )
    }
    pub fn label(&mut self, name: &str, stmt: JavaId) -> JavaId {
        self.push(NodeData::Label { name: name.into() }, vec![stmt])
    }
    pub fn assert_(&mut self, cond: JavaId, msg: Option<JavaId>) -> JavaId {
        let mut ch = vec![cond];
        ch.extend(msg);
        self.push(NodeData::Assert, ch)
    }
    pub fn raw(&mut self, text: &str) -> JavaId {
        self.push(NodeData::Raw { text: text.into() }, vec![])
    }

    // ---- builder:表达式 ----

    pub fn bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> JavaId {
        self.push(NodeData::Binary { op }, vec![l, r])
    }
    pub fn un(&mut self, op: UnOp, e: JavaId) -> JavaId {
        self.push(NodeData::Unary { op }, vec![e])
    }
    pub fn call(&mut self, callee: JavaId, args: Vec<JavaId>) -> JavaId {
        let mut ch = vec![callee];
        ch.extend(args);
        self.push(NodeData::Call, ch)
    }
    pub fn member(&mut self, obj: JavaId, name: &str) -> JavaId {
        let sym = self.intern_sym(name);
        self.push(NodeData::Member { name: sym }, vec![obj])
    }
    pub fn index(&mut self, arr: JavaId, idx: JavaId) -> JavaId {
        self.push(NodeData::Index, vec![arr, idx])
    }
    pub fn cast(&mut self, ty: JType, e: JavaId) -> JavaId {
        self.push(NodeData::Cast { ty }, vec![e])
    }
    pub fn paren(&mut self, e: JavaId) -> JavaId {
        self.push(NodeData::Paren, vec![e])
    }
    pub fn ternary(&mut self, c: JavaId, a: JavaId, b: JavaId) -> JavaId {
        self.push(NodeData::Ternary, vec![c, a, b])
    }
    pub fn new_(&mut self, ty: JType, args: Vec<JavaId>) -> JavaId {
        self.push(NodeData::New { ty, anon_raw: None }, args)
    }
    pub fn new_anon(&mut self, ty: JType, args: Vec<JavaId>, body_raw: String) -> JavaId {
        self.push(
            NodeData::New {
                ty,
                anon_raw: Some(body_raw),
            },
            args,
        )
    }
    pub fn new_array(
        &mut self,
        ty: JType,
        dims: u16,
        sized: u16,
        sizes: Vec<JavaId>,
        init: Option<JavaId>,
    ) -> JavaId {
        let mut ch = sizes;
        ch.extend(init);
        self.push(NodeData::NewArray { ty, dims, sized }, ch)
    }
    /// 合成分组(无作用域)。
    pub fn group(&mut self, stmts: Vec<JavaId>) -> JavaId {
        self.push(NodeData::Group, stmts)
    }
    pub fn array_lit(&mut self, elems: Vec<JavaId>) -> JavaId {
        self.push(NodeData::ArrayLit, elems)
    }
    pub fn var(&mut self, name: &str) -> JavaId {
        let sym = self.intern_sym(name);
        self.push(NodeData::VarRef { name: sym }, vec![])
    }
    pub fn lit(&mut self, l: Lit) -> JavaId {
        self.push(NodeData::Literal(l), vec![])
    }
    fn build_int_impl(&mut self, v: i64, long: bool) -> JavaId {
        if long {
            self.lit(Lit::Long(v))
        } else {
            self.lit(Lit::Int(v))
        }
    }
    pub fn this(&mut self) -> JavaId {
        self.push(NodeData::This, vec![])
    }
    pub fn super_(&mut self) -> JavaId {
        self.push(NodeData::Super, vec![])
    }
    pub fn instance_of(&mut self, e: JavaId, ty: JType, bind: Option<&str>) -> JavaId {
        self.push(
            NodeData::InstanceOf {
                ty,
                bind: bind.map(Into::into),
            },
            vec![e],
        )
    }
    pub fn lambda(&mut self, params_raw: &str, body: JavaId) -> JavaId {
        self.push(
            NodeData::Lambda {
                params_raw: params_raw.into(),
            },
            vec![body],
        )
    }
    pub fn method_ref(&mut self, receiver: JavaId, name: &str) -> JavaId {
        let sym = self.intern_sym(name);
        self.push(NodeData::MethodRef { name: sym }, vec![receiver])
    }

    // ---- 便捷 ----

    /// `obj.name(args…)` 调用。
    pub fn method_call(&mut self, obj: JavaId, name: &str, args: Vec<JavaId>) -> JavaId {
        let m = self.member(obj, name);
        self.call(m, args)
    }
    /// 裸方法调用 `name(args…)`(隐式 this / static)。
    pub fn plain_call(&mut self, name: &str, args: Vec<JavaId>) -> JavaId {
        let f = self.var(name);
        self.call(f, args)
    }

    /// 作用域解析后的 VarRef 类型(prepare 之后有效)。
    pub fn var_type(&self, id: JavaId) -> Option<&JType> {
        self.var_types.get(id.0 as usize).and_then(|t| t.as_ref())
    }

    /// root 子树内是否存在「两侧都可能为 String」的 ==/!=(引用比较)?
    /// 折叠守卫用:new String(lit)/常量链等产出池化字面量会改变 == 语义。
    /// 结果按 (root, prepare 代次) 缓存——prepare 每次改树后清空,
    /// 首次查询全扫一次,同轮内所有守卫调用复用。
    /// 多根扫描:任一根存在 String 身份比较。
    pub fn any_string_identity(&self, roots: &[JavaId]) -> bool {
        roots.iter().any(|&r| {
            let hit = Self::scan_string_identity(self, r);
            if hit {
                self.string_identity.set(Some((r, true)));
            }
            hit
        })
    }

    pub fn has_string_identity_compare(&self, root: JavaId) -> bool {
        if let Some((r, v)) = self.string_identity.get() {
            if r == root {
                return v;
            }
        }
        let v = Self::scan_string_identity(self, root);
        self.string_identity.set(Some((root, v)));
        v
    }

    fn scan_string_identity(&self, root: JavaId) -> bool {
        let mut stack = vec![root];
        while let Some(id) = stack.pop() {
            if let NodeData::Binary { op: BinOp::Eq | BinOp::Ne } = self.data(id) {
                let ch = self.children(id);
                if ch.len() == 2
                    && self.expr_maybe_string(ch[0])
                    && self.expr_maybe_string(ch[1])
                {
                    return true;
                }
            }
            for &c in self.children(id) {
                stack.push(c);
            }
        }
        false
    }

    /// `==`/`!=` 的这个操作数可能持有 String 引用吗?(保守近似)
    fn expr_maybe_string(&self, id: JavaId) -> bool {
        match self.data(id) {
            NodeData::Literal(Lit::Str(_)) => true,
            // null/this/数组/instanceof/数值布尔字面量:折叠不可能改变其 == 结果
            NodeData::Literal(_) | NodeData::This | NodeData::Super
            | NodeData::NewArray { .. } | NodeData::ArrayLit | NodeData::InstanceOf { .. } => false,
            NodeData::VarRef { .. } => self.var_type(id).map_or(true, Self::type_maybe_string),
            NodeData::Cast { ty } => Self::type_maybe_string(ty),
            NodeData::Paren => self
                .children(id)
                .first()
                .map_or(true, |&c| self.expr_maybe_string(c)),
            // 拼接:任一操作数 String ⇒ 结果 String;其余二元运算结果必为原始类型
            NodeData::Binary { op } if *op == BinOp::Add => {
                self.children(id).iter().any(|&c| self.expr_maybe_string(c))
            }
            NodeData::Binary { .. } => false,
            NodeData::Ternary => self
                .children(id)
                .get(1..)
                .map_or(true, |cs| cs.iter().any(|&c| self.expr_maybe_string(c))),
            // Call/Member/Index/MethodRef/Raw/Unary/Lambda…:类型未知或引用 → 保守视为可能
            _ => true,
        }
    }

    /// 类型可能持有 String 引用吗?
    fn type_maybe_string(t: &JType) -> bool {
        match t {
            JType::Bool | JType::Byte | JType::Short | JType::Int | JType::Long
            | JType::Char | JType::Float | JType::Double | JType::Void
            | JType::Array(_) => false, // 数组引用不是 String 本体(元素访问走 Index 节点)
            JType::Var => true,         // var / 推断 / 未知
            JType::Ref(n) => {
                let base = n.split('<').next().unwrap_or(n).trim();
                let last = base.rsplit('.').next().unwrap_or(base);
                matches!(
                    last,
                    "String" | "Object" | "CharSequence" | "Comparable" | "Serializable"
                ) || n.contains('<') // 泛型容器:剥壳后无法判定 → 保守
                // 单字母大写:类型参数 T/E/R(无界,运行期可为 String)
                || (last.len() <= 2 && last.chars().next().is_some_and(|c| c.is_uppercase()))
            }
        }
    }


    /// 设置当前方法的参数类型表(作为根作用域参与解析)。
    pub fn set_param_scope(&mut self, params: &[(String, JType)]) {
        self.param_scope = params.to_vec();
    }

    /// 名字是否为当前方法参数(虚拟执行的字段写守卫——参数写不得
    /// 误记为字段写)。
    pub fn is_param_name(&self, name: &str) -> bool {
        self.param_scope.iter().any(|(n, _)| n == name)
    }

    /// 名字键 → 名字反查(虚拟执行的字段写材料化)。
    /// 注入外部事实(前端 IR 权威陈述)。编译单元生命周期——换单元
    /// 前必须 clear_facts(simplify_unit / simplify_unit_with_facts
    /// 门面自动管理)。重复注入覆盖。
    pub fn set_facts(&mut self, facts: JavaFacts) {
        self.facts = Some(facts);
    }
    /// 清空外部事实(回到纯源码保守模式)。
    pub fn clear_facts(&mut self) {
        self.facts = None;
    }

    pub fn name_of_key(&self, k: u32) -> Option<String> {
        Some(self.names.name(k).to_string())
    }
    pub fn clear_param_scope(&mut self) {
        self.param_scope.clear();
    }
}

// ---------------------------------------------------------------------------
// Lang 实现
// ---------------------------------------------------------------------------

/// 类型名的基名(剥泛型实参 + 取最后点分段):`List<String>` → `List`、
/// `java.util.Map<K,V>` → `Map`。接收位类型绑定守卫用(同名构造视为
/// 静态类型一致;注解/显式类型实参前缀的罕见形态不匹配 → 保守拒绝)。
fn type_base_name(n: &str) -> &str {
    let no_args = match n.find('<') {
        Some(i) => &n[..i],
        None => n,
    };
    no_args.rsplit('.').next().unwrap_or(no_args)
}

// ---------------------------------------------------------------------------
// JLS 整数折叠辅助(从 cure-engine 迁入——数值语义归属语言侧)
// ---------------------------------------------------------------------------

/// i64 算术(long 语义):除/余除数为 0 不折(ArithmeticException)。
/// 移位量按 JLS 掩码(long: & 63)。
fn fold_i64(op: BinOp, a: i64, b: i64) -> Option<i64> {
    use BinOp::*;
    Some(match op {
        Add => a.wrapping_add(b),
        Sub => a.wrapping_sub(b),
        Mul => a.wrapping_mul(b),
        Div => {
            if b == 0 {
                return None;
            }
            a.wrapping_div(b)
        }
        Rem => {
            if b == 0 {
                return None;
            }
            a.wrapping_rem(b)
        }
        Shl => a.wrapping_shl((b as u64 & 63) as u32),
        Shr => a.wrapping_shr((b as u64 & 63) as u32),
        UShr => ((a as u64).wrapping_shr((b as u64 & 63) as u32)) as i64,
        BitAnd => a & b,
        BitXor => a ^ b,
        BitOr => a | b,
        _ => return None,
    })
}

/// i32 算术(int 语义):操作数按 i32 回绕;移位量按 JLS 掩码(& 31)。
fn fold_i32(op: BinOp, a: i64, b: i64) -> Option<i64> {
    use BinOp::*;
    let (x, y) = (a as i32, b as i32);
    Some(match op {
        Add => x.wrapping_add(y) as i64,
        Sub => x.wrapping_sub(y) as i64,
        Mul => x.wrapping_mul(y) as i64,
        Div => {
            if y == 0 {
                return None;
            }
            x.wrapping_div(y) as i64
        }
        Rem => {
            if y == 0 {
                return None;
            }
            x.wrapping_rem(y) as i64
        }
        Shl => x.wrapping_shl((y as u32) & 31) as i64,
        Shr => x.wrapping_shr((y as u32) & 31) as i64,
        UShr => (x as u32).wrapping_shr((y as u32) & 31) as i64,
        BitAnd => (x & y) as i64,
        BitXor => (x ^ y) as i64,
        BitOr => (x | y) as i64,
        _ => return None,
    })
}

/// 数值比较折叠(整数/字符提升为 int 后比较)。
fn cmp_i64(op: BinOp, x: i64, y: i64) -> Option<bool> {
    use BinOp::*;
    Some(match op {
        Lt => x < y,
        Le => x <= y,
        Gt => x > y,
        Ge => x >= y,
        Eq => x == y,
        Ne => x != y,
        _ => return None,
    })
}

impl Lang for JavaAst {
    type Id = JavaId;
    type NameKey = u32;

    fn kind(&self, id: JavaId) -> NodeKind {
        JavaAst::kind(self, id)
    }

    fn children(&self, id: JavaId) -> &[JavaId] {
        JavaAst::children(self, id)
    }

    fn set_child(&mut self, parent: JavaId, index: usize, new: JavaId) {
        self.nodes[parent.0 as usize].children.set(index, new);
    }
    fn remove_child(&mut self, parent: JavaId, index: usize) {
        self.nodes[parent.0 as usize].children.remove(index);
    }
    fn splice(&mut self, node: JavaId, index: usize, remove: usize, insert: Vec<JavaId>) {
        self.nodes[node.0 as usize].children.splice(index, remove, insert);
    }

    fn build_return(&mut self, value: Option<JavaId>) -> JavaId {
        self.ret(value)
    }
    fn build_block(&mut self, stmts: Vec<JavaId>) -> JavaId {
        self.block(stmts)
    }
    fn build_unary(&mut self, op: UnOp, operand: JavaId) -> JavaId {
        self.un(op, operand)
    }
    fn build_bool(&mut self, v: bool) -> JavaId {
        self.lit(Lit::Bool(v))
    }
    fn build_int(&mut self, v: i64, long: bool) -> JavaId {
        self.build_int_impl(v, long)
    }
    fn build_str(&mut self, s: &str) -> JavaId {
        self.lit(Lit::Str(s.to_string()))
    }
    fn build_char(&mut self, c: char) -> JavaId {
        self.lit(Lit::Char(c))
    }
    fn build_bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> JavaId {
        self.bin(op, l, r)
    }
    fn build_ternary(&mut self, c: JavaId, a: JavaId, b: JavaId) -> JavaId {
        self.ternary(c, a, b)
    }
    fn build_assign(&mut self, target: JavaId, value: JavaId) -> JavaId {
        self.assign(target, value)
    }

    // ---- 字面量折叠语义(JLS;引擎侧只做形态匹配)----

    fn fold_lit_bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> Option<JavaId> {
        let ll = self.literal(l)?;
        let rr = self.literal(r)?;
        // 字符串拼接(Str 与 Str/Char/Int/Long/Bool 字面量——拼接的隐式
        // valueOf 对这些基元是确定性的;浮点除外:Double.toString 算法与
        // Rust Display 不保证逐位一致)
        if op == BinOp::Add {
            let str_of = |x: LitRef<'_>| -> Option<String> {
                match x {
                    LitRef::Str(s) => Some(s.to_string()),
                    LitRef::Char(c) => Some(c.to_string()),
                    LitRef::Int(v) => Some(v.to_string()),
                    LitRef::Long(v) => Some(v.to_string()),
                    LitRef::Bool(b) => Some(b.to_string()),
                    _ => None,
                }
            };
            if matches!(ll, LitRef::Str(_)) || matches!(rr, LitRef::Str(_)) {
                if let (Some(a), Some(b)) = (str_of(ll), str_of(rr)) {
                    return Some(self.build_str(&format!("{a}{b}")));
                }
            }
        }
        // 整数折叠(JLS 环绕):1+2→3、2*3→6、1<<3→8、& | ^ 等。
        // 除/余除数为 0 不折叠(ArithmeticException 语义)。
        let int_of = |x: LitRef<'_>| -> Option<i64> {
            match x {
                // Java 中 char 参与算术时提升为 int
                LitRef::Int(v) | LitRef::Long(v) => Some(v),
                LitRef::Char(c) => Some(c as u32 as i64),
                _ => None,
            }
        };
        let (a, b) = (int_of(ll)?, int_of(rr)?);
        let wide = matches!(ll, LitRef::Long(_)) || matches!(rr, LitRef::Long(_));
        let v = if wide { fold_i64(op, a, b) } else { fold_i32(op, a, b) }?;
        Some(self.build_int(v, wide))
    }

    fn fold_lit_cmp(&mut self, op: BinOp, l: JavaId, r: JavaId) -> Option<bool> {
        let ll = self.literal(l)?;
        let rr = self.literal(r)?;
        // 整数/字符按数值比较(char 与 int 比较同样提升为 int)
        let int_of = |x: LitRef<'_>| -> Option<i64> {
            match x {
                LitRef::Int(v) | LitRef::Long(v) => Some(v),
                LitRef::Char(c) => Some(c as u32 as i64),
                _ => None,
            }
        };
        if let (Some(x), Some(y)) = (int_of(ll), int_of(rr)) {
            return cmp_i64(op, x, y);
        }
        // 布尔相等(装箱引用 == 语义上不可折,但布尔字面量场景下
        // true/false 是规范实例,值比较与引用比较一致)
        if let (LitRef::Bool(x), LitRef::Bool(y)) = (ll, rr) {
            if matches!(op, BinOp::Eq | BinOp::Ne) {
                return Some(if op == BinOp::Eq { x == y } else { x != y });
            }
        }
        // 字符串(引用比较)与浮点(NaN)不折
        None
    }

    fn fold_lit_neg(&mut self, v: i64, wide: bool) -> Option<i64> {
        // `-(-2147483648)`:i32 域内无 +2147483648,拒绝折叠
        let n = -v;
        if !wide && n > i32::MAX as i64 {
            return None;
        }
        Some(n)
    }

    fn reassoc_delta(
        &mut self,
        op1: BinOp,
        k1: i64,
        op2: BinOp,
        k2: i64,
        wide: bool,
    ) -> Option<ReassocOutcome<JavaId>> {
        use BinOp::*;
        let raw: i64 = match (op1, op2) {
            (BitXor, BitXor) => k1 ^ k2,
            (a, b) if matches!(a, Add | Sub) && matches!(b, Add | Sub) => {
                let sign = |o: BinOp| if o == Sub { -1i64 } else { 1i64 };
                // 宽整数域回绕:JLS long 加法模 2^64;debug 下的 i64
                // 溢出会 panic(`(x + 9223372036854775806L) + 2L`)
                sign(a)
                    .wrapping_mul(k1)
                    .wrapping_add(sign(b).wrapping_mul(k2))
            }
            _ => return None,
        };
        // JLS 宽度回绕:i32 合并结果溢出必须回绕——不回绕会产出
        // 非法字面量(`x + 2147483648` 无法作为 int 编译)
        let d = if wide { raw } else { raw as i32 as i64 };
        if d == 0 {
            return Some(ReassocOutcome::Neutral);
        }
        if op1 == BitXor && op2 == BitXor {
            return Some(ReassocOutcome::Lit(self.build_int(d, wide), BitXor));
        }
        if d > 0 {
            Some(ReassocOutcome::Lit(self.build_int(d, wide), Add))
        } else {
            // 负 delta 用 `x - |d|`;|d| 回绕后仍为负(仅 ±2^31 边界)
            // 或宽整数 -2^63 取反溢出时,退回 `x + d`(负字面量)——
            // 二者模 2^宽度 同余,语法均合法
            let nd = if wide { d.checked_neg().unwrap_or(d) } else { (d as i32).wrapping_neg() as i64 };
            if nd > 0 {
                Some(ReassocOutcome::Lit(self.build_int(nd, wide), Sub))
            } else {
                Some(ReassocOutcome::Lit(self.build_int(d, wide), Add))
            }
        }
    }

    fn copy_subtree(&mut self, id: JavaId) -> JavaId {
        let old_children: Vec<JavaId> = self.nodes[id.0 as usize].children.as_slice().to_vec();
        let mut children = Vec::with_capacity(old_children.len());
        for c in old_children {
            children.push(self.copy_subtree(c));
        }
        let data = self.nodes[id.0 as usize].data.clone();
        self.push(data, children)
    }

    fn effect(&self, id: JavaId) -> Effect {
        if let Some(e) = self.effect_cache.get(id.0 as usize).and_then(|x| *x) {
            return e;
        }
        // 缓存未命中(结构不变量被破坏时)退化为按需递归
        let mut e = self.own_effect(id);
        for &c in self.nodes[id.0 as usize].children.as_slice() {
            e = e.worst(self.effect(c));
        }
        e
    }

    fn invalidate_effect(&mut self, id: JavaId) {
        if let Some(slot) = self.effect_cache.get_mut(id.0 as usize) {
            *slot = None;
        }
        // 事件索引同步失效:本节点子树若被改,其预计算事件已陈旧
        // (B3 脏标记 + 清空该槽——见 cure-tree EventStore)
        self.events.invalidate(id.0 as usize);
        if let Some(slot) = self.stmt_facts.get_mut(id.0 as usize) {
            *slot = None;
        }
        if let Some(slot) = self.stmt_kills.get_mut(id.0 as usize) {
            *slot = None;
        }
        if let Some(slot) = self.stmt_opq.get_mut(id.0 as usize) {
            *slot = None;
        }
    }

    fn debug_verify_events(&self, stmt: JavaId) {
        if !verify_events_on() {
            return;
        }
        if let Some(indexed) = self.region_events(stmt) {
            let Some(fresh) = cure_tree::collect_region_events(self, stmt) else {
                return;
            };
            // 比较(键+种类+节点)三元组序列
            let key = |e: &cure_engine::kind::RegionEvent<JavaId, u32>| {
                (e.key, matches!(e.kind, cure_engine::kind::EventKind::Use), e.node.0)
            };
            let a: Vec<_> = indexed.iter().map(key).collect();
            let b: Vec<_> = fresh.iter().map(key).collect();
            if a != b {
                eprintln!(
                    "[EVENTS-STALE] stmt #{} kind={:?}: indexed {} events, fresh {} events",
                    stmt.0, self.kind(stmt), a.len(), b.len()
                );
            }
        }
    }

    fn region_events(
        &self,
        id: JavaId,
    ) -> Option<&[cure_engine::kind::RegionEvent<JavaId, u32>]> {
        // 空序列 = 未索引(非语句节点或已失效)→ None 走原递归
        self.events.slot(id.0 as usize)
    }

    fn var_key(&self, id: JavaId) -> Option<u32> {
        self.names.var_key(id.0 as usize)
    }

    fn stmt_var_ref_count(&self, stmt: JavaId, key: u32) -> Option<u32> {
        let slot = self.stmt_facts.get(stmt.0 as usize)?.as_deref()?;
        Some(
            slot.binary_search_by_key(&key, |f| f.key)
                .ok()
                .map_or(0, |i| slot[i].refs),
        )
    }

    fn stmt_lambda_captures(&self, stmt: JavaId, key: u32) -> Option<bool> {
        let slot = self.stmt_facts.get(stmt.0 as usize)?.as_deref()?;
        Some(
            slot.binary_search_by_key(&key, |f| f.key)
                .ok()
                .is_some_and(|i| slot[i].lambda_cap),
        )
    }

    fn stmt_kill_event(&self, stmt: JavaId, key: u32) -> Option<StmtKill<JavaId>> {
        let slot = self.stmt_kills.get(stmt.0 as usize)?.as_deref()?;
        Some(match slot.binary_search_by_key(&key, |f| f.key) {
            Ok(i) => match slot[i].kind {
                1 => StmtKill::Read,
                2 => StmtKill::Shadow,
                _ => StmtKill::DomWrite(slot[i].assign),
            },
            Err(_) => StmtKill::None,
        })
    }

    fn stmt_mentions(&self, stmt: JavaId, key: u32) -> Option<bool> {
        let slot = self.stmt_facts.get(stmt.0 as usize)?.as_deref()?;
        Some(
            slot.binary_search_by_key(&key, |f| f.key)
                .ok()
                .is_some_and(|i| slot[i].refs > 0 || slot[i].shadow),
        )
    }

    fn stmt_opaque(&self, stmt: JavaId) -> Option<bool> {
        self.stmt_opq.get(stmt.0 as usize)?.map(|f| f.opaque)
    }

    // ---- 外部事实(cure-engine Lang 词汇的 Java 实现:查 facts 槽)----

    fn fact_single_def(&self, key: u32) -> bool {
        match (&self.facts, self.names.name(key)) {
            (Some(f), n) => f.var(n).single_def,
            _ => false,
        }
    }
    fn fact_single_use(&self, key: u32) -> bool {
        match (&self.facts, self.names.name(key)) {
            (Some(f), n) => f.var(n).single_use,
            _ => false,
        }
    }
    fn fact_bool_valued(&self, key: u32) -> bool {
        match (&self.facts, self.names.name(key)) {
            (Some(f), n) => f.var(n).bool_valued,
            _ => false,
        }
    }
    fn fact_call_effect(&self, name: &str) -> Option<cure_engine::Effect> {
        self.facts
            .as_ref()
            .and_then(|f| {
                f.call_effects.get(name).map(|set| {
                    set.iter().copied().fold(Effect::Pure, Effect::worst)
                })
            })
    }

    fn node_index(&self, id: JavaId) -> usize {
        id.0 as usize
    }
    fn id_of_index(&self, idx: usize) -> JavaId {
        JavaId(idx as u32)
    }

    fn is_opaque(&self, id: JavaId) -> bool {
        match self.data(id) {
            // 不可解析原文
            NodeData::Raw { .. } => true,
            // 匿名类体是原文(捕获的外部局部变量不可见)——
            // 对抗波 4:仅被匿名类捕获的变量曾被零用途规则误删
            NodeData::New { anon_raw: Some(_), .. } => true,
            _ => false,
        }
    }

    fn own_effect(&self, id: JavaId) -> Effect {
        match self.data(id) {
            NodeData::Literal(_)
            | NodeData::This
            | NodeData::Super
            | NodeData::Paren
            | NodeData::Block
            | NodeData::Group
            | NodeData::Empty
            | NodeData::VarDecl { .. }
            | NodeData::Break { .. }
            | NodeData::Continue { .. }
            | NodeData::Try
            | NodeData::Catch { .. }
            | NodeData::Switch
            | NodeData::Case { .. }
            | NodeData::Label { .. }
            | NodeData::ExprStmt
            | NodeData::Ternary
            | NodeData::ArrayLit
            | NodeData::Lambda { .. }
            | NodeData::MethodRef { .. }
            | NodeData::InstanceOf { .. } => Effect::Pure,
            NodeData::VarRef { .. } => Effect::MayRead,
            NodeData::Member { .. } => Effect::MayThrow,
            NodeData::Index => Effect::MayThrow,
            NodeData::New { .. } | NodeData::NewArray { .. } => Effect::MayThrow,
            NodeData::Call => Effect::Unknown,
            NodeData::Assign { .. } => Effect::MayWrite,
            NodeData::Unary { op } => {
                if op.is_incdec() {
                    Effect::MayWrite
                } else {
                    Effect::Pure
                }
            }
            NodeData::Binary { op } => {
                if op.may_div_zero() {
                    Effect::MayThrow
                } else {
                    Effect::Pure
                }
            }
            NodeData::Cast { ty } => {
                if ty.is_ref() {
                    // 引用转型可能抛 ClassCastException
                    Effect::MayThrow
                } else {
                    Effect::Pure
                }
            }
            NodeData::For { .. } | NodeData::ForEach { .. } | NodeData::If => Effect::MayRead,
            NodeData::While | NodeData::DoWhile => Effect::MayRead,
            NodeData::Return | NodeData::Yield => Effect::MayThrow,
            NodeData::Throw => Effect::MayThrow,
            NodeData::Assert => Effect::MayThrow,
            NodeData::Synchronized => Effect::MayWrite,
            NodeData::Raw { .. } => Effect::Unknown,
        }
    }

    fn is_bool(&self, id: JavaId) -> bool {
        match self.data(id) {
            NodeData::Literal(l) => matches!(l, Lit::Bool(_)),
            NodeData::Unary { op } => *op == UnOp::Not,
            NodeData::Binary { op } => {
                op.is_comparison() || matches!(op, BinOp::And | BinOp::Or)
            }
            NodeData::Ternary => {
                let ch = self.children(id);
                self.is_bool(ch[1]) && self.is_bool(ch[2])
            }
            NodeData::Paren => self.is_bool(self.children(id)[0]),
            NodeData::InstanceOf { .. } => true,
            NodeData::VarRef { .. } => self
                .var_types
                .get(id.0 as usize)
                .and_then(|t| t.as_ref())
                .is_some_and(|t| t.is_bool()),
            _ => false,
        }
    }

    fn is_exact_int(&self, id: JavaId) -> bool {
        match self.data(id) {
            NodeData::Literal(l) => matches!(
                l,
                Lit::Int(_) | Lit::Long(_) | Lit::Char(_) | Lit::NumRaw { val: NumVal::Int(_) | NumVal::Long(_), .. }
            ),
            NodeData::VarRef { .. } => {
                self.var_types.get(id.0 as usize).and_then(|t| t.as_ref()).is_some_and(|t| t.is_integral())
            }
            NodeData::Binary { op } => {
                let ch = self.children(id);
                (op.is_arith() || op.is_comparison())
                    && self.is_exact_int(ch[0])
                    && self.is_exact_int(ch[1])
            }
            NodeData::Cast { ty } => ty.is_integral(),
            NodeData::Paren => self.is_exact_int(self.children(id)[0]),
            NodeData::Ternary => {
                let ch = self.children(id);
                self.is_exact_int(ch[1]) && self.is_exact_int(ch[2])
            }
            _ => false,
        }
    }

    fn is_local_var(&self, id: JavaId) -> bool {
        // VarRef 必须解析到局部变量/参数(var_types 作用域解析)才可
        // 被传播/删除规则触碰——字段引用(this.x / 静态字段裸名)有
        // 跨方法可见性。未解析(Raw 区域/坏代码)同样返回 false(保守)。
        // 差分审查抓获:dead_store 曾据此把 `first = cl;`(静态字段写)
        // 当局部死存储消除,双向链表表头更新静默丢失。
        matches!(self.data(id), NodeData::VarRef { .. }) && self.var_type(id).is_some()
    }

    fn is_inferred_decl(&self, decl: JavaId) -> bool {
        matches!(self.data(decl), NodeData::VarDecl { ty: JType::Var, .. })
    }

    fn is_char_decl(&self, decl: JavaId) -> bool {
        matches!(self.data(decl), NodeData::VarDecl { ty: JType::Char, .. })
    }

    fn has_pattern_bindings(&self, root: JavaId) -> bool {
        let mut stack = vec![root];
        while let Some(n) = stack.pop() {
            if let NodeData::InstanceOf { bind: Some(_), .. } = self.data(n) {
                return true;
            }
            for &c in self.children(n) {
                stack.push(c);
            }
        }
        false
    }

    fn is_long_decl(&self, decl: JavaId) -> bool {
        matches!(self.data(decl), NodeData::VarDecl { ty: JType::Long, .. })
    }
    fn is_wide_decl(&self, decl: JavaId) -> bool {
        // 窄类型(byte/short/char)复合赋值含隐式收窄:delta 折回声明
        // 会产出超域非法常量或丢静态类型(char 99 → println 打数字)
        !matches!(
            self.data(decl),
            NodeData::VarDecl { ty: JType::Byte | JType::Short | JType::Char, .. }
        )
    }

    fn receiver_propagation_unsound(
        &self,
        decl: JavaId,
        value: JavaId,
        use_parent: Option<JavaId>,
        use_at_head: bool,
    ) -> bool {
        // 使用位必须是成员访问接收者(Member 的 child 0)——实参位
        // raw↔泛型双向兼容(unchecked),不敏感
        let is_receiver = matches!(use_parent, Some(p) if {
            matches!(self.data(p), NodeData::Member { .. }) && use_at_head
        });
        if is_receiver {
            // 装箱丢失(R13 P0-2):引用声明 + 基本类型值——接收位无装箱转换
            if self.receiver_boxing_unsound(decl, value) {
                return true;
            }
            // New 值接收位类型绑定守卫(R15 终审 P0-4/P0-5 抓获):
            // (a) diamond(`new ArrayList<>()`)——类型推断依赖赋值目标,
            //     脱离赋值位内联进接收位退化为无目标推断
            //     (MethodRefToInner 抓获:`List<String> l = new ArrayList<>();
            //     l.stream().forEach(TestString::new)` → Stream<Object>,
            //     构造器引用 Object 无法转 String);
            // (b) 构造类型基名 ≠ 声明类型基名——接收者静态类型改变,方法
            //     绑定与 throws 子句随之改变(CloneableProblem 抓获:
            //     `A a0 = new CloneableProblem(0); a0.clone()` 内联后
            //     A.clone() 的 CloneNotSupportedException 不再可抛,catch 失据)。
            if self.receiver_new_type_unsound(decl, value) {
                return true;
            }
            // 声明类型为 **已知泛型的 raw 引用**(无类型实参——擦除成员解析):
            // var 推断/显式带实参/非泛型/未知外部类型不触发(保守——未知名
            // 保持传播,与修复前行为一致)
            let raw_ref = match self.data(decl) {
                NodeData::VarDecl { ty: JType::Ref(n), .. } => {
                    !n.contains('<')
                        && n.rsplit('.').next().is_some_and(|base| {
                            self.raw_generic_names.contains(base)
                        })
                }
                _ => false,
            };
            if !raw_ref {
                return false;
            }
            // init 静态类型可能**带泛型**:调用(返回类型未知)或带实参 new
            match self.data(value) {
                NodeData::Call => true,
                NodeData::New { ty, .. } => {
                    matches!(ty, JType::Ref(n) if n.contains('<'))
                }
                _ => false,
            }
        } else {
            // 数组访问基座(Index 的 child 0):new 数组值的元素类型绑定
            // 守卫(R15 终审 P0-2 续,T8357653b 抓获)——声明元素带泛型
            // 实参(`B<?>[] b2 = new B[1]`)时内联进 `b2[0]` 基座使元素
            // 从 B<?> 变 raw B,成员解析走擦除(rett() 返回 Object →
            // `String s2 = …` 赋值失败)。元素名须**全串**一致(含实参),
            // 维度须与声明嵌套一致;var 推断声明恒安全
            if matches!(use_parent, Some(p) if {
                matches!(self.data(p), NodeData::Index) && use_at_head
            }) {
                return self.index_base_newarray_unsound(decl, value);
            }
            false
        }
    }

    fn bin_op(&self, id: JavaId) -> Option<BinOp> {
        match self.data(id) {
            NodeData::Binary { op } => Some(*op),
            _ => None,
        }
    }
    fn un_op(&self, id: JavaId) -> Option<UnOp> {
        match self.data(id) {
            NodeData::Unary { op } => Some(*op),
            _ => None,
        }
    }
    fn literal(&self, id: JavaId) -> Option<LitRef<'_>> {
        match self.data(id) {
            NodeData::Literal(l) => Some(match l {
                Lit::Bool(b) => LitRef::Bool(*b),
                Lit::Int(v) => LitRef::Int(*v),
                Lit::Long(v) => LitRef::Long(*v),
                Lit::Float(v) => LitRef::Float(*v),
                Lit::Double(v) => LitRef::Double(*v),
                Lit::Char(c) => LitRef::Char(*c),
                Lit::Str(s) => LitRef::Str(s),
                // TextBlock 的**值**需完整 JLS §3.10.6 处理(附加缩进
                // 剥离 + 行尾空白剔除 + \s 转义——InputJava14EscapedS
                // 抓获:按原始内文求值把 assert 折成 false)。存的是原文
                //(往返保真),值语义未实现 → 保守不当作字面量(折叠拒
                // 绝;打印路径不经过 literal())
                Lit::TextBlock(_) => return None,
                Lit::NumRaw { val, .. } => match val {
                    NumVal::Int(v) => LitRef::Int(*v),
                    NumVal::Long(v) => LitRef::Long(*v),
                    NumVal::Float(v) => LitRef::Float(*v),
                    NumVal::Double(v) => LitRef::Double(*v),
                },
                Lit::Null => LitRef::Null,
            }),
            _ => None,
        }
    }
    fn var_name(&self, id: JavaId) -> Option<&str> {
        match self.data(id) {
            NodeData::VarRef { name } => Some(self.sn(*name)),
            NodeData::VarDecl { name, .. } => Some(name),
            NodeData::ForEach { name, .. } | NodeData::Catch { name, .. } => Some(name),
            _ => None,
        }
    }
    fn member_name(&self, id: JavaId) -> Option<&str> {
        match self.data(id) {
            NodeData::Member { name } => Some(self.sn(*name)),
            NodeData::MethodRef { name } => Some(self.sn(*name)),
            _ => None,
        }
    }
    fn double_not_sound(&self, _operand: JavaId) -> bool {
        // Java:`!` 操作数必须 boolean(JLS 15.15.6)——可编译的 !!x
        // 其 x 恒 boolean,恒等式无条件成立(hard_obfuscation 的
        // !!it.hasNext() 迭代器形态依赖此折叠)
        true
    }

    fn var_cast_position_ok(&self, value: JavaId, typed: JavaId) -> bool {
        // 值须为 VarRef(其余形态自带类型,不走本裁决)
        if !matches!(self.data(value), NodeData::VarRef { .. }) {
            return true;
        }
        // 声明类型未解析(自由名——反编译残缺作用域常态)→ 未知类型
        // 可 cast 到任何引用类型,恒可编译(golden redundant_typed_cast:
        // `String s = value; return (String) s;` → `(String) value` 保留)
        let Some(d) = self.var_type(value).cloned() else {
            return true;
        };
        let t = match self.data(typed) {
            NodeData::Cast { ty } => ty.clone(),
            NodeData::InstanceOf { ty, .. } => ty.clone(),
            _ => return true,
        };
        if d == t {
            return true;
        }
        // Object 任一侧 → cast/instanceof 恒合法;其余已解析的不同名
        // 类型(基元混搭/两个具体引用)→ interface/子类关系不可判,
        // 保守拒绝(ddc weixin dt/k 的原始守卫语义保持)
        let obj = |ty: &JType| {
            matches!(ty, JType::Ref(r) if r == "java.lang.Object" || r == "Object")
        };
        obj(&d) || obj(&t)
    }
    fn is_field_name(&self, name: &str) -> bool {
        self.field_types.contains_key(name)
    }
    fn payload_equal(&self, a: JavaId, b: JavaId) -> bool {
        // 字面量:值级比较(NumRaw 的 0x1F 与 Int 的 31 值等可折——
        // data 全等会退化成原文等)
        if let (NodeData::Literal(x), NodeData::Literal(y)) = (self.data(a), self.data(b)) {
            return lit_value_eq(x, y);
        }
        self.data(a) == self.data(b)
    }
    fn assign_op(&self, id: JavaId) -> Option<BinOp> {
        match self.data(id) {
            NodeData::Assign { op } => *op,
            _ => None,
        }
    }

    /// 重建缓存:
    /// 1) 效果表——arena 按升序扫描(child index < parent index 不变量);
    /// 2) 变量类型表——从 root 做作用域栈遍历。
    fn prepare(&mut self, root: JavaId) {

        if cnt_prepare_on() {
            PREPARE_CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
            PREPARE_NODES.fetch_add(self.nodes.len(), std::sync::atomic::Ordering::Relaxed);
        }
        // 名字键增量扩展(cure-tree NameTable):新节点 intern(既有节点
        // 键不变——名字与位置无关)。名字先收集(&self 借用安全形态),
        // 再批量扩展(&mut self.names)。
        let n_nodes = self.nodes.len();
        let key_slots = self.names.key_slots();
        if key_slots < n_nodes {
            let names: Vec<Option<String>> = (key_slots..n_nodes)
                .map(|i| self.var_name(JavaId(i as u32)).map(|n| n.to_string()))
                .collect();
            self.names.extend_owned(key_slots, &names);
        }
        // 守卫缓存失效:树已变
        self.string_identity.set(None);
        // 区域事件索引【惰性重建】(B3,cure-tree EventStore):只重建
        // **空条目**——被 invalidate_effect 失效的语句、或新语句。未失效
        // 条目的事件是子树局部的(与位置无关)。关键不变量:**一切**改树
        // 路径都必须失效受影响语句的条目:
        //   - 立即 Replace:目标祖先链 + with 子树中既有节点的**旧容器**
        //     祖先链(搬移——pass.rs 立即路径两处都做)
        //   - 队列编辑应用:同上(pass.rs deferred 应用后统一失效)
        // 违反不变量的后果:陈旧事件驱动错误决策(B2 教训:cff_diamond
        // 差分当场抓获)。性能背景:Types.java 上 prepare 每 pass 全量重建
        // = 726 倍冗余(37 万文件语料 prepare 占 57% 线程时间)。
        self.events.resize(n_nodes);
        self.stmt_facts.resize_with(n_nodes, || None);
        self.stmt_kills.resize_with(n_nodes, || None);
        self.stmt_opq.resize_with(n_nodes, || None);
        if self.events.is_clean() {
            if self.prepared_root == Some(root) {
                return;
            }
            // 树全局缓存(事件/效果表)仍有效;根变了 → 只重建
            // root 相关的作用域分析段
            self.rebuild_var_types(root);
            self.prepared_root = Some(root);
            return;
        }
        self.prepared_root = Some(root);
        self.events.mark_clean();
        for sr in cure_tree::stmt_roots(self, root) {
            let idx = sr.0 as usize;
            if self.events.slot(idx).is_none() {
                if let Some(events) = cure_tree::collect_region_events(self, sr) {
                    self.events.put(idx, events);
                }
            }
            if self.stmt_facts.get(idx).map_or(true, |f| f.is_none()) {
                let facts = self.collect_stmt_facts(sr);
                self.stmt_facts[idx] = Some(facts);
            }
            if self.stmt_kills.get(idx).map_or(true, |f| f.is_none()) {
                let kills = self.collect_stmt_kill_facts(sr);
                self.stmt_kills[idx] = Some(kills);
            }
            if self.stmt_opq.get(idx).map_or(true, |f| f.is_none()) {
                if let Some(opq) = self.stmt_opaque_fact(sr) {
                    self.stmt_opq[idx] = Some(opq);
                }
            }
        }
        // ---- 效果表(cure-tree rebuild_effects:不动点 sweep——
        // children-先于-parent 索引序 + 注入节点违例多轮收敛)----
        let effects = cure_tree::rebuild_effects(self, n_nodes);
        self.effect_cache = effects;
        self.rebuild_var_types(root);
    }
}

impl JavaAst {
    /// One traversal of a statement subtree: every VarRef increments its
    /// name key; a VarRef under any Lambda also marks the name as
    /// lambda-captured. Sorted by key for binary-search queries.
    fn collect_stmt_facts(&self, root: JavaId) -> Box<[StmtFact]> {
        let mut counts: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
        let mut caps: std::collections::HashSet<u32> = std::collections::HashSet::new();
        let mut shadows: std::collections::HashSet<u32> = std::collections::HashSet::new();
        let mut stack: Vec<(JavaId, bool)> = vec![(root, false)];
        while let Some((id, in_lambda)) = stack.pop() {
            let node = &self.nodes[id.0 as usize];
            let is_lambda = matches!(node.data, NodeData::Lambda { .. });
            let is_named_decl = matches!(
                node.data,
                NodeData::VarDecl { .. } | NodeData::ForEach { .. } | NodeData::Catch { .. }
            );
            if matches!(node.data, NodeData::VarRef { .. }) {
                if let Some(key) = self.names.var_key(id.0 as usize) {
                    *counts.entry(key).or_insert(0) += 1;
                    if in_lambda {
                        caps.insert(key);
                    }
                }
            } else if is_named_decl {
                if let Some(key) = self.names.var_key(id.0 as usize) {
                    shadows.insert(key);
                }
            }
            let child_in_lambda = in_lambda || is_lambda;
            for &c in node.children.as_slice() {
                stack.push((c, child_in_lambda));
            }
        }
        let mut keys: std::collections::HashSet<u32> = counts.keys().copied().collect();
        keys.extend(shadows.iter().copied());
        let mut facts: Vec<StmtFact> = keys
            .into_iter()
            .map(|key| StmtFact {
                key,
                refs: counts.get(&key).copied().unwrap_or(0),
                lambda_cap: caps.contains(&key),
                shadow: shadows.contains(&key),
            })
            .collect();
        facts.sort_unstable_by_key(|f| f.key);
        facts.into_boxed_slice()
    }

    /// Whether the statement subtree contains an opaque node.
    fn stmt_opaque_fact(&self, root: JavaId) -> Option<StmtOpq> {
        let mut stack = vec![root];
        while let Some(id) = stack.pop() {
            if self.is_opaque(id) {
                return Some(StmtOpq { opaque: true });
            }
            for &c in self.nodes[id.0 as usize].children.as_slice() {
                stack.push(c);
            }
        }
        Some(StmtOpq { opaque: false })
    }

    /// One DFS for all names in the statement (first event wins), sorted
    /// by key for binary-search queries.
    fn collect_stmt_kill_facts(&self, root: JavaId) -> Box<[StmtKillFact]> {
        let mut facts: Vec<StmtKillFact> = cure_engine::rules::collect_stmt_kill_events(self, root)
            .into_iter()
            .map(|(key, event)| match event {
                StmtKill::None => unreachable!("collector only records real events"),
                StmtKill::Read => StmtKillFact {
                    key,
                    kind: 1,
                    assign: JavaId(0),
                },
                StmtKill::Shadow => StmtKillFact {
                    key,
                    kind: 2,
                    assign: JavaId(0),
                },
                StmtKill::DomWrite(assign) => StmtKillFact {
                    key,
                    kind: 3,
                    assign,
                },
            })
            .collect();
        facts.sort_unstable_by_key(|f| f.key);
        facts.into_boxed_slice()
    }

    /// root 相关的作用域分析(从 prepare 提取——根变化时单独重建)。
    pub(crate) fn rebuild_var_types(&mut self, root: JavaId) {
        // ---- 变量类型(作用域栈)----
        self.var_types.clear();
        self.var_types.resize(self.nodes.len(), None);
        #[derive(Clone)]
        enum Frame {
            Enter(JavaId),
            Exit,
            PopScope,
            Bind(String, JType),
        }
        let mut base: HashMap<String, JType> = HashMap::new();
        for (n, t) in &self.param_scope {
            base.insert(n.clone(), t.clone());
        }
        let mut scopes: Vec<HashMap<String, JType>> = vec![base];
        let mut stack = vec![Frame::Enter(root)];
        while let Some(f) = stack.pop() {
            match f {
                Frame::Enter(id) => {
                    let node = &self.nodes[id.0 as usize];
                    enum Action {
                        None,
                        Scope,
                        Bind(String, JType),
                    }
                    let action = match &node.data {
                        NodeData::Block | NodeData::For { .. } | NodeData::Try => Action::Scope,
                        NodeData::VarDecl { name, ty, .. } => {
                            scopes.last_mut().unwrap().insert(name.clone(), ty.clone());
                            Action::None
                        }
                        NodeData::VarRef { name } => {
                            if let Some(t) = scopes
                                .iter()
                                .rev()
                                .find_map(|s| s.get(self.sn(*name)))
                                .cloned()
                            {
                                self.var_types[id.0 as usize] = Some(t);
                            }
                            Action::None
                        }
                        NodeData::Catch { name, .. } => {
                            Action::Bind(name.clone(), JType::Var)
                        }
                        NodeData::ForEach { name, ty } => {
                            Action::Bind(name.clone(), ty.clone())
                        }
                        _ => Action::None,
                    };
                    // 期望弹出顺序:[Bind?] children… [PopScope?] Exit
                    // ⇒ 压栈顺序取反:Exit、[PopScope]、children(逆序)、[Bind]
                    stack.push(Frame::Exit);
                    if matches!(action, Action::Scope | Action::Bind(_, _)) {
                        scopes.push(HashMap::new());
                        stack.push(Frame::PopScope);
                    }
                    for &c in node.children.as_slice().iter().rev() {
                        stack.push(Frame::Enter(c));
                    }
                    if let Action::Bind(n, t) = action {
                        stack.push(Frame::Bind(n, t));
                    }
                }
                Frame::Bind(name, ty) => {
                    scopes.last_mut().unwrap().insert(name, ty);
                }
                Frame::PopScope => {
                    scopes.pop();
                }
                Frame::Exit => {}
            }
        }
    }
}

// ---------------------------------------------------------------------------
// 区域事件索引(使用索引):规则区域扫描的预计算。
// 遍历序必须与引擎 scan_region 原递归严格一致:
//   Assign      → [Write(目标)] + value 子树(目标本身不再当 Use 扫)
//   自增自减    → [Write(目标)],不扫子
//   VarDecl     → [Write, Shadow] + 全部孩子
//   ForEach     → [Write, Shadow] + 全部孩子;Catch → [Shadow] + 孩子
//   VarRef      → [Use]
//   其余        → 依序拼接孩子
// ---------------------------------------------------------------------------

impl JavaAst {
    /// 局部传播到成员访问接收位的**装箱丢失**检查:声明为引用类型 +
    /// 值是基本类型形态(原始字面量/基本类型变量/算术一元二元——
    /// R13 P0-2 抓获:`Object o = +c; o.getClass()` 曾内联成
    /// `(+c).getClass()` / 常量传播折成 `65.getClass()`——int 接收者
    /// 不可解引用;装箱转换只发生在赋值/传参位,接收位无转换)。
    /// 字符串字面量/引用变量合法(`"a".length()`)。
    /// 接收位 `new` 值的类型可靠性(R15 终审 P0-4/P0-5):diamond 无目标
    /// 推断不可靠;构造类型基名 ≠ 声明类型基名(接口/父类型声明的实现类
    /// 构造)→ 接收者静态类型改变 → 方法绑定/throws 子句可能改变 →
    /// 不可靠。同名构造(`Foo f = new Foo(); f.m()`)与 var 推断声明
    ///(推断类型即 init 类型)保持传播。
    fn receiver_new_type_unsound(&self, decl: JavaId, value: JavaId) -> bool {
        let NodeData::New { ty, .. } = self.data(value) else {
            return false;
        };
        let decl_ty = match self.data(decl) {
            NodeData::VarDecl { ty, .. } => ty,
            _ => return false,
        };
        // var:推断类型 == init 静态类型——New 与之恒等(含 diamond:
        // `var l = new ArrayList<>()` 本就按无目标推断)
        if matches!(decl_ty, JType::Var) {
            return false;
        }
        let name = match ty {
            JType::Ref(n) => n.as_str(),
            // 非引用构造类型(不可能出现在合法 new;防御拒绝)
            _ => return true,
        };
        // diamond:推断依赖赋值目标——接收位无目标
        if name.ends_with("<>") {
            return true;
        }
        match decl_ty {
            JType::Ref(d) => type_base_name(d) != type_base_name(name),
            // 声明为数组/基元却以类构造为 init——恒不可靠(防御)
            _ => true,
        }
    }

    /// 数组访问基座(`b2[0]` 的 b2 位)`new` 数组值的元素类型可靠性
    /// (R15 终审 P0-2 续,T8357653b 抓获):声明元素类型与 new 的元素
    /// 类型须**全串一致**(含泛型实参——`B<?>[] b2 = new B[1]` 内联使
    /// b2[0] 从 B<?> 变 raw B → 成员解析擦除化),维度数须与声明嵌套
    /// 一致。var 推断声明(推断类型即 init 类型)恒安全。
    fn index_base_newarray_unsound(&self, decl: JavaId, value: JavaId) -> bool {
        let NodeData::NewArray { ty, dims, .. } = self.data(value) else {
            return false;
        };
        let decl_ty = match self.data(decl) {
            NodeData::VarDecl { ty, .. } => ty,
            _ => return false,
        };
        if matches!(decl_ty, JType::Var) {
            return false;
        }
        // 声明的数组嵌套层数 + 最内元素类型(Ref 全串含泛型实参)
        let mut elem: &JType = decl_ty;
        let mut nest = 0u16;
        while let JType::Array(inner) = elem {
            elem = inner.as_ref();
            nest += 1;
        }
        nest != *dims || *elem != *ty
    }

    fn receiver_boxing_unsound(&self, decl: JavaId, value: JavaId) -> bool {
        let declared_ref = match self.data(decl) {
            NodeData::VarDecl { ty, .. } => ty.is_ref(),
            _ => false,
        };
        if !declared_ref {
            return false;
        }
        match self.data(value) {
            NodeData::Literal(
                Lit::Int(_) | Lit::Long(_) | Lit::Float(_) | Lit::Double(_) | Lit::Bool(_)
                | Lit::Char(_),
            ) => true,
            // 基本类型变量(var_type 解析参数/局部声明类型)
            NodeData::VarRef { .. } => matches!(
                self.var_type(value),
                Some(
                    JType::Int
                        | JType::Long
                        | JType::Byte
                        | JType::Short
                        | JType::Char
                        | JType::Float
                        | JType::Double
                        | JType::Bool
                )
            ),
            // 算术/逻辑一元二元:结果必为基本类型
            NodeData::Unary { .. } | NodeData::Binary { .. } => true,
            // 三元:结果可为基本类型(R14 族 B 抓获:`Object o = true ?
            // 'a' : 'b'; o.hashCode()` 曾内联成 'a'.hashCode() 不可解引用)
            // ——含引用臂的保守拒绝(过度面小)
            NodeData::Ternary => true,
            // 基本类型 cast:(int) 'a' 结果 int——引用 cast 保守同拒
            NodeData::Cast { .. } => true,
            // instanceof:结果恒 boolean
            NodeData::InstanceOf { .. } => true,
            // 装箱类型声明 + 调用/成员/下标值(返回类型未知——s.charAt(0)
            // 等基本返回调用内联进接收位丢装箱——R14 终审 P0-2 抓获:
            // `Character c = s.charAt(0); return c.toString()` 曾产出
            // `s.charAt(0).toString()` 无法取消引用 char)。装箱名精确
            // 匹配,非装箱引用类型(Foo f = m(); f.x())不受限
            NodeData::Call | NodeData::Member { .. } | NodeData::Index => {
                match self.data(decl) {
                    NodeData::VarDecl { ty: JType::Ref(n), .. } => {
                        let boxed = [
                            "Character", "Integer", "Long", "Boolean", "Double",
                            "Float", "Byte", "Short", "Number", "AtomicInteger",
                            "AtomicLong",
                        ];
                        n.rsplit('.').next().is_some_and(|b| boxed.contains(&b))
                    }
                    _ => false,
                }
            }
            _ => false,
        }
    }

    /// 名字 → 实化键(持久表,只增;同名字恒同键)。
    /// intern 名字 → Sym(复用 name_intern 表 + sym_names 向量)。
    pub fn intern_sym(&mut self, name: &str) -> Sym {
        Sym(self.names.intern(name))
    }

    /// Sym → 名字原文。
    pub fn sn(&self, sym: Sym) -> &str {
        self.names.name(sym.0)
    }




}


// ---------------------------------------------------------------------------
// 结构化指标:编译单元级聚合(供 --stats 等评估简化效果——节点数 /
// 判定点 / 嵌套深度不受格式化影响,只被语义简化改变)
// ---------------------------------------------------------------------------

/// 编译单元全部可执行体(方法/构造器/初始化块,含嵌套类型)的
/// 结构指标总和。
pub fn unit_metrics(ast: &JavaAst, unit: &CompilationUnit) -> TreeMetrics {
    let mut total = TreeMetrics::default();
    for ty in &unit.types {
        metrics_of_type(ast, ty, &mut total);
    }
    total
}

fn metrics_of_type(ast: &JavaAst, ty: &TypeDecl, total: &mut TreeMetrics) {
    for m in &ty.members {
        match m {
            Member::Method { body: Some(b), .. } | Member::Constructor { body: Some(b), .. } => {
                total.add(subtree_metrics(ast, *b));
            }
            Member::Initializer { body, .. } => {
                total.add(subtree_metrics(ast, *body));
            }
            Member::Type(t) => metrics_of_type(ast, t, total),
            _ => {}
        }
    }
}

static ENV_CACHED: std::sync::OnceLock<(bool, bool)> = std::sync::OnceLock::new();
fn env_flags() -> (bool, bool) {
    *ENV_CACHED.get_or_init(|| {
        (
            std::env::var("CURE_CNT_PREPARE").is_ok(),
            std::env::var("CURE_VERIFY_EVENTS").is_ok(),
        )
    })
}
fn cnt_prepare_on() -> bool { env_flags().0 }
fn verify_events_on() -> bool { env_flags().1 }

pub static PREPARE_CALLS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
pub static PREPARE_NODES: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);

pub static COLLECT_CALLS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);

// ---------------------------------------------------------------------------
// 测试
// ---------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn builder_smoke() {
        let mut a = JavaAst::new();
        let one = a.lit(Lit::Int(1));
        let x = a.var_decl("x", JType::Int, Some(one));
        let xv = a.var("x");
        let r = a.ret(Some(xv));
        let body = a.block(vec![x, r]);
        assert_eq!(a.kind(body), NodeKind::Block);
        assert_eq!(a.children(body).len(), 2);
    }

    #[test]
    fn effect_aggregation() {
        let mut a = JavaAst::new();
        let f = a.plain_call("foo", vec![]);
        let r = a.ret(Some(f));
        let body = a.block(vec![r]);
        a.prepare(body);
        assert_eq!(a.effect(f), Effect::Unknown);
        assert_eq!(a.effect(r), Effect::Unknown);
        assert_eq!(a.effect(body), Effect::Unknown);

        let mut b = JavaAst::new();
        let two = b.lit(Lit::Int(2));
        let three = b.lit(Lit::Int(3));
        let add = b.bin(BinOp::Add, two, three);
        let r2 = b.ret(Some(add));
        let body2 = b.block(vec![r2]);
        b.prepare(body2);
        assert_eq!(b.effect(add), Effect::Pure);
        // return 语句本身按控制流计为 MayThrow,Block 聚合随之
        assert_eq!(b.effect(r2), Effect::MayThrow);
        assert_eq!(b.effect(body2), Effect::MayThrow);
    }

    #[test]
    fn var_type_resolution_and_shadowing() {
        let mut a = JavaAst::new();
        // { boolean b = true; { int b2 = 1; b2; } b; }
        let one = a.lit(Lit::Int(1));
        let b2_decl = a.var_decl("b2", JType::Int, Some(one));
        let inner_b2 = a.var("b2");
        let inner_stmt = a.expr_stmt(inner_b2);
        let inner = a.block(vec![b2_decl, inner_stmt]);
        let tv = a.lit(Lit::Bool(true));
        let decl_b = a.var_decl("b", JType::Bool, Some(tv));
        let outer_b = a.var("b");
        let outer_stmt = a.expr_stmt(outer_b);
        let body = a.block(vec![decl_b, inner, outer_stmt]);
        a.prepare(body);
        assert!(a.is_bool(outer_b));
        // b2 的使用在 inner 块内,类型可解析
        assert!(a.var_type(inner_b2).is_some());
    }

    #[test]
    fn raw_and_foreach_semantics() {
        let mut a = JavaAst::new();
        // { int x = 1; RAW; for (String s : list) { s.len(); } }
        let one = a.lit(Lit::Int(1));
        let dx = a.var_decl("x", JType::Int, Some(one));
        let r = a.raw("broken ~!@ code");
        let sv = a.var("s");
        let call = a.method_call(sv, "len", vec![]);
        let es = a.expr_stmt(call);
        let body = a.block(vec![es]);
        let lst = a.var("list");
        let fe = a.for_each("s", JType::Ref("String".into()), lst, body);
        let all = a.block(vec![dx, r, fe]);
        a.prepare(all);
        assert_eq!(a.kind(r), NodeKind::Raw);
        assert_eq!(a.effect(r), Effect::Unknown);
        assert!(a.var_type(sv).is_some()); // 循环变量类型可解析
    }

    #[test]
    fn num_raw_literal() {
        let mut a = JavaAst::new();
        let l = a.lit(Lit::NumRaw {
            text: "0x1F".into(),
            val: NumVal::Int(31),
        });
        a.prepare(l);
        assert!(a.is_exact_int(l));
        assert!(matches!(a.literal(l), Some(LitRef::Int(31))));
    }
}