chore: 清理超长历史注释,并一并提交并行会话的代码改动

注释清理(65 文件 / 约 -2578 行 / 约 516 处):删除/压缩叙述式历史注释——多行根因叙事、工作代号段(D25/D30/N11/B5/S4/阶段E 等)、改前改后与先例叙述、逐条推导块,压成一行讲清作用信息;契约类注释(用途、参数与返回、关键约束与不变量、IR 结构说明)与被注释掉的代码一律保留

存疑 标记 6 处一字未动:StubMergerStore.vp、VpiReader.vp、VLogger.vp、AssignGenMixin.vp、CallGenLib.vp、CallGenLeafKit.vp

本次一并带入并行会话的代码改动(同批文件,按文件粒度无法拆分):fileio.File -> rowptr[fileio.File] 等

已知副作用/残留:① CallGenDispatch.vp 有 2 行非注释行的行尾被 LF->CRLF 规范化(纯字节变化,无文本改动);② core/Gen 区约 24 处压缩因并行会话整文件覆写被写回原样,尚未生效(StmtGenCifMixin/ClassGenVTable/FuncGenDef/ClassGenEnum/StructGenMixin/TypeSpec 等)

清单与复跑判据见 Debugger/comment_cleanup_summary.md(该目录不受版本控制)
This commit is contained in:
TermiNexus committed 2026-09-16 11:27:05 +08:00
1 parent b28f20cce7
commit 7cba4cbc95
65 files changed
+1233 -3292

No files matched your search

+64 -147
View File
@@ -32,11 +32,8 @@ import lib.core.Gen.GenApi as GenApi
# ============================================================
# _InjectedCreatesCycle - 容器模块自动注入前的破环检查
#
# 注入 _list/_dict 到当前模块的 _imported_modules 前,检查被注入模块自身的
# 依赖是否已含当前模块。若命中(如 _list 显式 import memhub,而 memhub 因
# `__provides__: list[str]` 注解要注入 _list)→ memhub↔_list 循环依赖 →
# Phase A 多遍收敛死锁(gfx.vp "无法完成翻译" 根因)。命中则跳过注入:去掉
# 环边后,_list 已因当前模块被其依赖而处于闭包,list 类仍由 Phase1b 注册。
# 注入 _list/_dict 前检查被注入模块自身的依赖是否已含当前模块,命中(如 memhub↔_list)则跳过注入——
# 否则 Phase A 多遍收敛死锁("无法完成翻译");去掉环边后 _list 仍因当前模块被其依赖而处于闭包,list 类仍由 Phase1b 注册。
# ============================================================
def _InjectedCreatesCycle(entry: IncludesScanner.FileEntry | t.CPtr,
mod_name: str) -> int:
@@ -149,7 +146,7 @@ def _RewriteDepsTxt(temp_dir: str, sha1: str, deps_str: str) -> None:
dpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "deps.txt")
if dpath is None:
return
df: fileio.File | t.CPtr = fileio.File(dpath, fileio.MODE.W)
df: rowptr[fileio.File] = fileio.File(dpath, fileio.MODE.W)
if not df.closed:
dl: sizet = string.strlen(deps_str)
if dl > 0:
@@ -230,18 +227,12 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
# ============================================================
# _BuildReachableResult - 从 Sha1Store 构建可达 FileEntry 列表
#
# 依赖图按需扫描(StubMergerBuild._BuildReachableSha1Set)只登记
# "可达 includes + App"到全局 Sha1Store;本函数把 incdir 非空条目
# (includes)重建为 Phase1 1a/1b-pre/1b 需要的 FileEntry 列表。
# App 条目(incdir 空,仅登记不做可达文件处理)不进入 result——
# Phase B 已编译其 .obj,加入会导致 Phase B+ 重复编译 → 重复符号。
# 依赖图按需扫描(StubMergerBuild._BuildReachableSha1Set)只把「可达 includes + App」登记到全局 Sha1Store;
# 本函数把 incdir 非空条目(includes)重建为 Phase1 1a/1b-pre/1b 需要的 FileEntry 列表。
# App 条目(incdir 空)不进入 result——Phase B 已编译其 .obj,加入会导致 Phase B+ 重复编译 → 重复符号。
#
# Args:
# mb: 内存池
# includes_dir: includes 根目录(仅用于失败诊断,路径以 store 为准)
#
# Returns:
# ScanResult 指针(stdlib 分配,调用方负责释放);失败返回 None
# Args: mb 内存池;includes_dir includes 根目录(仅用于失败诊断,路径以 store 为准)
# Returns: ScanResult 指针(stdlib 分配,调用方负责释放);失败返回 None
# ============================================================
def _BuildReachableResult(mb: rowptr[memhub.MemBuddy],
includes_dir: str) -> IncludesScanner.ScanResult | t.CPtr:
@@ -314,26 +305,18 @@ def _BuildReachableResult(mb: rowptr[memhub.MemBuddy],
return result
# ============================================================
# Phase1Main - Phase1 拆分(阶段 E):Phase1Main
# Phase1Main - Phase1 主流程(扫描 → 预注册 → 1a/1b 翻译 → 生成 stub)
#
# 直接移动模式:函数迁至此模块,原 Phase1.vp 仅保留常量/_mbuddy;
# 调用方经本模块前缀访问。全局符号经 Phase1._xxx 跨模块访问。
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
# ============================================================
# ============================================================
# RunPhase1 - Phase1: 扫描 includes 目录,按需翻译并生成 stub
#
# 对每个 .vp/.py 文件检查 stub 是否已存在,若不存在则翻译并分离 stub。
# "按需翻译":仅对 stub 不存在的文件执行翻译,避免重复工作。
# 对每个 .vp/.py 文件检查 stub 是否已存在,不存在才翻译并分离 stub(避免重复工作)。
#
# Args:
# mb: 内存池
# includes_dir: includes 目录路径
# temp_dir: 临时目录(保存 stub)
# log: 日志器
#
# Returns:
# 0 成功,非 0 失败
# Args: mb 内存池;includes_dir includes 目录路径;temp_dir 临时目录(保存 stub);log 日志器
# Returns: 0 成功,非 0 失败
# ============================================================
def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
log: VLogger.Logger | t.CPtr) -> int:
@@ -345,9 +328,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# 设置全局 temp_dir(供跨模块 CDefine 查找使用)
GenApi.set_temp_dir(temp_dir)
# 同步设置 HandlesExprCall 的 temp_dir(供 vpi stub 返回注解缓存按需加载)。
# 必须在 includes 翻译前设置:Phase1 翻译 json 等 includes 时,"同模块方法
# 调用(定义在后)"(如 json.__setitem__ 调 _pool_alloc)需要读
# {temp_dir}/{sha1[0:2]}/{sha1}.vpi 兜底,而 Phase2 的 SetTempDir 太晚。
# 必须在 includes 翻译前设置:Phase1 翻译 includes 时「同模块方法调用(定义在后)」需读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi 兜底,Phase2 的 SetTempDir 太晚。
GenApi.SetTempDir(temp_dir)
# 确保 temp 目录存在(build_dir/temp 可能尚未创建)
@@ -356,17 +337,12 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
log.banner("Phase1: 扫描 includes(按需翻译)")
# 初始化 AST 表(只需一次):必须在依赖图 BFS 扫描之前调用。
# _init_tables 懒分配关键字/操作符表(全局 _kw_head 等),若首次触发
# 发生在 BFS 的某个文件级 mark 之后,reset_to 会回收表节点 → 全局
# 悬空 → 后续 tokenize 的 _kw_lookup strcmp 访问 0x4 崩溃(1.log ASan)。
# _init_tables 懒分配的关键字/操作符表(全局 _kw_head 等)若首次触发在 BFS 的文件级 mark 之后,reset_to 会回收表节点 → 全局悬空 → 后续 tokenize 崩溃。
ast._init_tables(mb)
# 依赖图按需扫描(App 优先模型):
# 1) 先扫 App 源文件(Config.SourceDir)的 import 依赖 → 初始队列
# 2) BFS 沿队列按文件系统解析 includes 文件,只对可达 includes
# 读文件+算 SHA1+解析 import,补充依赖图并登记 Sha1Store/
# deps 存储器/写 deps.txt
# 3) 未引用的 includes 从不读取(根本不扫描)
# 依赖图按需扫描(App 优先模型):先扫 App 源文件(Config.SourceDir)的 import 依赖作为初始队列,
# BFS 按文件系统解析 includes 文件(只对可达者读文件+算 SHA1+解析 import)并登记 Sha1Store/deps/写 deps.txt;
# 未引用的 includes 从不读取。
t_scan0: uint64 = w32.win32base.GetTickCount64()
reachable_set: str = None
reachable_count: int = 0
@@ -425,26 +401,16 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# ============================================================
# Phase 1a: 注册可达 includes 文件的 struct/enum/union
#
# 解决字母序依赖问题:按字母顺序翻译时,后面的 struct 未注册
# 前面就需要用。Phase 1a 先注册所有 struct/enum/union 到全局表,
# Phase 1b 全量翻译时 struct 已注册,走 existing 路径只翻译方法体。
# 只处理可达文件,避免翻译不需要的 includes(如 Test 不依赖 ast 模块)。
#
# 拓扑排序:预扫描所有文件收集类继承关系,按依赖顺序处理文件,
# 确保父类所在文件先于子类所在文件处理(如 base.vp 先于 astaux.vp)。
# 解决字母序依赖问题:1a 先把所有 struct/enum/union 注册到全局表,1b 全量翻译时 struct 已注册、走 existing 路径只翻译方法体;
# 只处理可达文件;处理前按类继承关系拓扑排序,确保父类所在文件先于子类所在文件。
# ============================================================
p1a_reg: int = 0
p1a_skp: int = 0
p1a_fl: int = 0
# ============================================================
# [临时取证打点] Phase1 早期扁平缓冲分配失败现场快照(定位后清理)。
# 现场:class_names_buf/parent_names_buf 均成功,def_sha1s_buf 却返回 NULL;
# 此时主池(1GiB)远未耗尽 → 非 OOM。本打点仅只读 mb 状态 + 打印,
# 绝不改变任何分配行为与逻辑。三维度判定:
# ① OOM:dump_stats 显示 used≈total、free≈0;
# ② mb 损坏:usable 基址/大小荒谬、self_check != 0、max_order 异常;
# ③ size/order 异常:本次请求 size 算出的 order > max_order。
# [临时取证打点] Phase1 早期扁平缓冲分配失败现场快照(定位后清理);仅只读 mb 状态 + 打印,不改变任何分配行为与逻辑。
# 三维度判定:① OOM(dump_stats 显示 used≈total、free≈0);② mb 损坏(usable 基址/大小荒谬、self_check != 0);③ 本次请求 size 算出的 order > max_order。
# 用 VLogger.info(非终止;VLogger.error 会 sys.exit(1) 掩盖后续打点)。
# ============================================================
if mb is not None:
@@ -486,7 +452,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
f"[取证] order(class_names {p1_sz_cn}B+16)={p1_o_cn} fl={p1_fl_cn} "
f"order(def_sha1s {p1_sz_ds}B+16)={p1_o_ds} fl={p1_fl_ds} "
f"order(topo {p1_sz_tp}B+16)={p1_o_tp} order(box {p1_sz_bx}B+16)={p1_o_bx} "
f"(fl==0 只说明该阶空、仍可由更高阶拆分;若 alloc 失败而 dump_stats 的 free>0 才是元数据损坏铁证,"
f"(fl==0 只说明该阶空、仍可由更高阶拆分;若 alloc 失败而 dump_stats 的 free>0 才是元数据损坏铁证,"
f"任一 order>max_order={mb.max_order} 则是 size 异常)",
"Phase1")
@@ -504,13 +470,9 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
string.memset(generic_file_count_box, 0, 4)
topo_count: int = 0
# 三块扁平缓冲是 Phase1Scan._ScanClassInheritance 的契约前置缓冲:函数
# 入口已对 NULL 做 fail-fast 检查(本处分配失败若静默跳过整段,将退化为
# 字母序并掩盖 OOM 等内存故障)。故分配失败时逐块显式报错终止(VLogger.error
# 内部 sys.exit(1)),绝不静默回退。
# 注:topo_order/topo_count_box 仅供后续 Phase1Topo._TopoSortFiles 使用,
# 不属 _ScanClassInheritance 契约,故不并入此处终止逻辑;其有效性由
# _TopoSortFiles 入口自身校验(out_order/out_count 为 NULL 即返回 -1 并终止)。
# 三块扁平缓冲是 Phase1Scan._ScanClassInheritance 的契约前置缓冲:该函数入口对 NULL fail-fast(静默跳过整段会退化为字母序并掩盖 OOM 等内存故障),
# 故分配失败时逐块显式报错终止(VLogger.error 内部 sys.exit(1)),绝不静默回退。
# 注:topo_order/topo_count_box 仅供 Phase1Topo._TopoSortFiles 使用,有效性由其入口自身校验(NULL 即返回 -1 并终止),不并入此处终止逻辑。
if class_names_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败:class_names_buf 为 NULL,终止编译", "Phase1")
if parent_names_buf is None:
@@ -559,14 +521,8 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# ============================================================
# Phase 1a-pre-generic-all: mark1 前全量预注册 includes 泛型模板
#
# 1a-pre-generic 只覆盖"可达"泛型文件;非可达泛型文件(如 FuncTest
# 场景的 linkedlist/llvmlite/_list)的模板要等 Phase2 Phase A-pre-inc
# (mark2 之后)才注册 → reset 回收表/节点 → _find_generic_template
# 此处对全部 includes 轻量预注册,使模板表与节点都落在 mark1 之前。
# result 只是"可达子集",非可达的 _list/_dict/linkedlist/llvmlite 等泛型文件
# 模板不会注册 → list[int]/dict[...] 特化失败(ast/base.vp _dump_op_list 根因)。
# _PreRegisterAllGenericTemplates 内有 generic marker 文本预筛,全量扫描
# 不 parse 全部 AST,仅对含 marker 的文件轻量处理,OOM 风险可控。
# 非可达泛型文件(_list/_dict/linkedlist/llvmlite 等)的模板若在 Phase2(mark2 之后)才注册会被 reset 回收 → list[int]/dict[...] 特化失败。
# 故对全部 includes 轻量预注册,使模板表与节点都落在 mark1 之前(_PreRegisterAllGenericTemplates 内有 generic marker 文本预筛,不 parse 全部 AST,OOM 风险可控)。
full_scan_g: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir)
if full_scan_g is not None:
Phase1Scan._PreRegisterAllGenericTemplates(mb, full_scan_g)
@@ -576,14 +532,9 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# ============================================================
# Phase 1a: 注册 struct/enum/union(mark1 前,全量 includes)
#
# OOM 治理:1a 对全部 includes parse 完整 AST(主 pool 分配),若
# 驻留到 mark1(循环后)会累积 98 文件 × 每文件 AST 树 → 1GB 主池
# 耗尽(NewAstTest alloc 失败 0xBE 根因)。现改为:
# 1) StdlibAlloc=1:1a 注册的跨文件持久数据(StructTable 表、
# Fields/Name/ModuleSha1、struct_ty 深拷贝、枚举/Renum/泛型
# 模板等)全部落在 perm_pool(永不 reset),不占主 pool;
# 2) 每文件翻译完 reset_to(文件级 mark):仅回收该文件的 AST/
# lexer/token/IR 等瞬时数据,注册数据不受影响。
# OOM 治理:1a 对全部 includes parse 完整 AST,若驻留到 mark1 会累积至 1GB 主池耗尽。故:
# 1) StdlibAlloc=1:1a 注册的跨文件持久数据(StructTable 表、Fields/Name/ModuleSha1、struct_ty 深拷贝、枚举/Renum/泛型模板等)全部落 perm_pool(永不 reset);
# 2) 每文件翻译完 reset_to(文件级 mark):只回收该文件的 AST/lexer/token/IR 等瞬时数据,注册数据不受影响。
# mark1 仍在 1a 结束后打,1b 每文件 reset_to(mark1) 语义不变。
# ============================================================
GenApi.set_stdlib_alloc(1)
@@ -630,7 +581,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# 读取文件内容
file_path_r: str = entry_r.Path
f_r: fileio.File | t.CPtr = fileio.File(file_path_r, fileio.MODE.R)
f_r: rowptr[fileio.File] = fileio.File(file_path_r, fileio.MODE.R)
if f_r.closed:
p1a_fl += 1
continue
@@ -707,19 +658,10 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
stdlib.free(tr_r._nonlocal_names)
stdlib.free(src_buf_r)
# [TEMP-DIAG] 1a 每文件哨兵(定位越界写发生在哪个阶段):校验本文件窗口
# [p1a_mark, chk_depth) 内所有存活块的块尾 canary。
# 位置必须在 reset_to 之前:reset_to 会把 chk_depth 截断回 p1a_mark
# (includes/memhub.vp reset_to L540 / 截断循环 L558-559),之后再查同一
# mark 时 from_mark >= chk_depth 直接 return 0(includes/memhub.vp
# check_alive_blocks L1234-1235)→ 窗口为空、必然漏报。
# p1a_mark 在循环体首行捕获(本文件 L597),故窗口恰为"本文件
# parse + translate(declare=1)"期间分配的块。mark 语义与既有 3 处哨兵
# (L1109/L1135/L1226 均传 mark1 = 各自窗口起点)一致。
# 仅 --detection-deep(mb.deep_detect != 0)时执行:本哨兵每文件一次、
# 每次遍历整窗口活块(O(n)),默认全程不进入,回到上一轮之前的开销。
# 读法同本文件既有 mb.detection(L470):直接读 MemBuddy 实例的公开
# int 字段(mb: rowptr[memhub.MemBuddy])。
# [TEMP-DIAG] 1a 每文件哨兵:校验本文件窗口 [p1a_mark, chk_depth) 内所有存活块的块尾 canary。
# 位置必须在 reset_to 之前:reset_to 会把 chk_depth 截断回 p1a_mark,之后再查同一 mark 时 check_alive_blocks 直接 return 0(窗口为空、必然漏报)。
# p1a_mark 在循环体首行捕获(本文件 L597),故窗口恰为「本文件 parse + translate(declare=1)」期间分配的块。
# 仅 --detection-deep(mb.deep_detect != 0)时执行(每文件 O(n) 遍历活块);读法同本文件既有 mb.detection:直接读 MemBuddy 实例的公开 int 字段。
if mb.deep_detect != 0:
chk_rc_1a: int = mb.check_alive_blocks(p1a_mark)
if chk_rc_1a != 0:
@@ -754,11 +696,8 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# ============================================================
# Phase 1b: 全量翻译(struct 已注册,走 existing 路径翻译方法体)
# ============================================================
# OOM 回收:1a-pre/泛型预注册/1a 全部结束后打 mark1,
# 所有跨文件持久表(_struct_table/_enum_table/_generic_class_nodes/
# _renum_*/关键字表等)都落在 mark1 之前。
# 1b 每文件翻译完后 reset_to(mark1) 回收该文件的 AST/IR/Handler/dump 临时,
# 避免 90 文件累积导致 1GB 池 OOM。
# OOM 回收:mark1 在 1a-pre/泛型预注册/1a 全部结束后打,所有跨文件持久表(_struct_table/_enum_table/_generic_class_nodes/_renum_*/关键字表等)都落在 mark1 之前;
# 1b 每文件翻译完后 reset_to(mark1) 回收该文件的 AST/IR/Handler/dump 临时,避免 90 文件累积导致 1GB 池 OOM。
mark1: sizet = mb.mark()
# 1b 阶段新注册的 struct/enum 条目改 stdlib 分配(跨文件永久存活,不被 reset 回收)
@@ -767,11 +706,8 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# ============================================================
# Phase 1b-pre: 预生成所有待翻译文件的 .vpi stub
#
# 依赖模块的 .vpi 必须在翻译前可用:1b 主循环按扫描顺序翻译(非拓扑),
# 若 argparse 先于 hashtable 翻译,hashtable.vpi 尚未生成,
# LoadDepVpiIntoModule 读不到 → declare 未注入 → 跨模块调用
# 返回类型推断错误(如 HashTable.__getitem__ 被推断为 Ptr(HashTable)
# 而 define 返回 i8*)→ 运行时返回值截断崩溃。
# 依赖模块的 .vpi 必须在翻译前可用:1b 主循环按扫描顺序(非拓扑)翻译,若依赖模块后翻译则 LoadDepVpiIntoModule 读不到 .vpi
# → declare 未注入 → 跨模块调用的返回类型推断错误(如误推为 Ptr 而 define 返回 i8*)→ 运行时返回值截断崩溃。
# ============================================================
p1bpre_failed: int = 0
dir_len_pv: sizet = string.strlen(temp_dir)
@@ -797,7 +733,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {entry_pv.Path} | vpi 路径生成失败", "Phase1")
continue
vf_pv: fileio.File | t.CPtr = fileio.File(vpi_path_pv, fileio.MODE.R)
vf_pv: rowptr[fileio.File] = fileio.File(vpi_path_pv, fileio.MODE.R)
if not vf_pv.closed:
vf_pv.close()
stdlib.free(vpi_path_pv)
@@ -805,7 +741,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
stdlib.free(vpi_path_pv)
# 读取文件、解析 AST、生成 vpi
file_path_pv: str = entry_pv.Path
f_pv: fileio.File | t.CPtr = fileio.File(file_path_pv, fileio.MODE.R)
f_pv: rowptr[fileio.File] = fileio.File(file_path_pv, fileio.MODE.R)
if f_pv.closed:
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 无法打开源文件", "Phase1")
@@ -854,7 +790,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
if pyi_pos_pv > 0:
pyi_path_pv: str = StubMergerCollect._sliced_path(temp_dir, dir_len_pv, sha1_pv, "vpi")
if pyi_path_pv is not None:
pf_pv: fileio.File | t.CPtr = fileio.File(pyi_path_pv, fileio.MODE.W)
pf_pv: rowptr[fileio.File] = fileio.File(pyi_path_pv, fileio.MODE.W)
if not pf_pv.closed:
pf_pv.write(pyi_buf_pv, pyi_pos_pv)
pf_pv.close()
@@ -864,12 +800,8 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | pyi 缓冲分配失败(OOM)", "Phase1")
stdlib.free(src_buf_pv)
# [TEMP-DIAG] 1b-pre 每文件哨兵(同上,定位越界写发生在哪个阶段):
# 校验本文件窗口 [mark1, chk_depth) 内所有存活块的块尾 canary。
# 同样必须在 reset_to(mark1) 之前(否则窗口被截断为空,见 memhub.vp
# reset_to L558-559 与 check_alive_blocks L1234-1235)。mark1 在 1b 段前
# 捕获(本文件 L756),是 1b/1b-pre 的统一窗口起点;本循环无文件级 mark,
# 故与既有 L1109/L1135/L1226 同传 mark1。
# [TEMP-DIAG] 1b-pre 每文件哨兵(同上):校验本文件窗口 [mark1, chk_depth) 内所有存活块的块尾 canary。
# 同样必须在 reset_to(mark1) 之前(否则窗口被截断为空)。mark1 在 1b 段前捕获(本文件 L756),是 1b/1b-pre 的统一窗口起点。
# 仅 --detection-deep(mb.deep_detect != 0)时执行,读法同上方 1a 哨兵。
if mb.deep_detect != 0:
chk_rc_pv: int = mb.check_alive_blocks(mark1)
@@ -939,16 +871,12 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
continue
# 检查 stub 是否已存在(按需翻译:跳过已存在的)
# 例外:含 class 定义的模块必须翻译——类注册(register_struct)
# 只在翻译时发生;增量编译时 stub/text 已存在被跳过 → 跨模块类构造
# find_struct_by_module 落空 → 返回类型 fail-fast
# (fltk.Fl_Window(...) → "4049e14d73c7e091.Fl_Window 无法精确获取",
# 仅 --clean 全量翻译时可用)。扫描 1b-pre 生成的 vpi 是否有
# "class " 行(vpi 为解析后存根,精确无注释/字符串误判)。
# 例外:含 class 定义的模块必须翻译——类注册只在翻译时发生,增量编译跳过会导致跨模块类构造 find_struct_by_module 落空 → 返回类型 fail-fast。
# 判据:扫描 1b-pre 生成的 vpi 是否有 "class " 行(vpi 为解析后存根,精确无注释/字符串误判)。
force_translate: int = 0
vpi_path_1b: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi")
if vpi_path_1b is not None:
vf_1b: fileio.File | t.CPtr = fileio.File(vpi_path_1b, fileio.MODE.R)
vf_1b: rowptr[fileio.File] = fileio.File(vpi_path_1b, fileio.MODE.R)
if not vf_1b.closed:
vbuf_1b: bytes = stdlib.malloc(1048576)
if vbuf_1b is not None:
@@ -982,13 +910,13 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
VLogger.info(f"含 class 定义,强制翻译: {rp}(sha1={sha1})", "Phase1")
else:
# 检查 stub 是否已存在(按需翻译:跳过已存在的)
sf: fileio.File | t.CPtr = fileio.File(stub_path, fileio.MODE.R)
sf: rowptr[fileio.File] = fileio.File(stub_path, fileio.MODE.R)
if not sf.closed:
sf.close()
# 检查 text.ll 是否也存在(虚表扫描需要 text.ll)
text_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "text.ll")
if text_path is not None:
tf: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R)
tf: rowptr[fileio.File] = fileio.File(text_path, fileio.MODE.R)
if not tf.closed:
tf.close()
stdlib.free(text_path)
@@ -1010,7 +938,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# 读取文件内容
file_path: str = entry.Path
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
f: rowptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
if f is None:
VLogger.error(f"无法打开(f=None): {file_path}", "Phase1")
failed += 1
@@ -1061,10 +989,8 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# 计时点 1:读文件 + lexer/tokenize/parse 完成
t_f1: uint64 = w32.win32base.GetTickCount64()
# 文本扫描注册 CTypedef(stdint.vp 崩溃根因修复):AST 字符串字段
# (attr/id)token 零拷贝指向 src_buf,跨阶段内容不稳定(悬空),
# ScanCTypedefDeclarations 读 attr 会 0xC0000005。改用源码文本逐行
# 匹配 "NAME: t.CTypedef = TYPE",在 src_buf 有效期内注册。
# 文本扫描注册 CTypedef:AST 字符串字段(attr/id)token 零拷贝指向 src_buf,跨阶段内容不稳定(悬空),读 attr 会 0xC0000005;
# 故改用源码文本逐行匹配 "NAME: t.CTypedef = TYPE",在 src_buf 有效期内注册。
GenApi.RegisterCTypedefFromSource(mb, src_buf)
# 生成 .vpi 存根文件(直接遍历 AST,不依赖 PythonToStubConverter)
@@ -1072,7 +998,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# (消除 1b-pre 与 1b 对同一文件的重复 _GeneratePyiFromAst 工作)
vpi_path_1b: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi")
if vpi_path_1b is not None:
vf_1b: fileio.File | t.CPtr = fileio.File(vpi_path_1b, fileio.MODE.R)
vf_1b: rowptr[fileio.File] = fileio.File(vpi_path_1b, fileio.MODE.R)
vpi_exists_1b: int = 0
if not vf_1b.closed:
vf_1b.close()
@@ -1083,7 +1009,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
pyi_pos: sizet = StubConverter._GeneratePyiFromAst(
mb, tree, entry.RelPath, pyi_buf, Phase1.PYI_BUF_SIZE)
if pyi_pos > 0:
pf_1b: fileio.File | t.CPtr = fileio.File(vpi_path_1b, fileio.MODE.W)
pf_1b: rowptr[fileio.File] = fileio.File(vpi_path_1b, fileio.MODE.W)
if not pf_1b.closed:
pf_1b.write(pyi_buf, pyi_pos)
pf_1b.close()
@@ -1130,10 +1056,8 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
failed += 1
continue
# 诊断(builtins.vp dump 卡死根因排查):pre_rc 哨兵在 translate 前,
# 覆盖不到 translate 期间的 AST/IR 写穿。dump 前立即检查存活块
# header/canary(rc=6 块尾 canary 损坏=越界写穿数据区),把"dump
# 时链表成环卡死"变成"定位到 translate 期间写穿发生的文件"。
# 诊断:pre_rc 哨兵在 translate 前、覆盖不到 translate 期间的 AST/IR 写穿,故 dump 前立即检查存活块 header/canary
# (rc=6 块尾 canary 损坏 = 越界写穿数据区),把「dump 时链表成环卡死」变成「定位到 translate 期间写穿发生的文件」。
post_rc: int = mb.check_alive_blocks(mark1)
if post_rc != 0:
VLogger.error(f"内存损坏(translate后存活块, rc={post_rc}): {file_path}", "Phase1")
@@ -1158,7 +1082,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
dir_len_p1: sizet = string.strlen(temp_dir)
stub_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "stub.ll")
if stub_path_p1 is not None:
sf_p1: fileio.File | t.CPtr = fileio.File(stub_path_p1, fileio.MODE.W)
sf_p1: rowptr[fileio.File] = fileio.File(stub_path_p1, fileio.MODE.W)
if not sf_p1.closed:
sf_p1.write(stub_buf, stub_len)
sf_p1.close()
@@ -1181,7 +1105,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# save text.ll
text_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "text.ll")
if text_path_p1 is not None:
tf_p1: fileio.File | t.CPtr = fileio.File(text_path_p1, fileio.MODE.W)
tf_p1: rowptr[fileio.File] = fileio.File(text_path_p1, fileio.MODE.W)
if not tf_p1.closed:
tf_p1.write(text_buf, text_len)
tf_p1.close()
@@ -1195,7 +1119,7 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
# save dependencies(_imported_modules) for Phase B
deps_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "deps.txt")
if deps_path_p1 is not None:
df_p1: fileio.File | t.CPtr = fileio.File(deps_path_p1, fileio.MODE.W)
df_p1: rowptr[fileio.File] = fileio.File(deps_path_p1, fileio.MODE.W)
if not df_p1.closed:
if tr._imported_modules is not None:
dl: sizet = string.strlen(tr._imported_modules)
@@ -1214,23 +1138,16 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
translated += 1
# OOM 回收:回收本文件翻译产生的瞬时数据(AST/tokens/lexer/IR/Handler/dump 临时)
# 持久表(struct/enum/REnum/spec 缓存/全局函数名等)已迁出 reset 区间(stdlib),
# OOM 回收:回收本文件翻译产生的瞬时数据(AST/tokens/lexer/IR/Handler/dump 临时);持久表(struct/enum/REnum/spec 缓存/全局函数名等)已迁出 reset 区间(stdlib),
# reset_to(mark1) 只回收 mark1 之后分配、未被单独 free 的块。
#
# 内存哨兵(崩溃点漂移排查):reset 前检查存活块 header(抓越界写),
# reset 后检查 free list 完整性(抓链表损坏)。任一损坏立即终止,
# 把"随机崩溃漂移"变成"确定性定位到当前文件"。
# rc 语义(check_alive_blocks):1=块地址低于 usable,2=高于 usable+size,
# 3=order 为负,4=order 超 max_order,5=块未对齐到 order 大小,
# 6=块尾 canary 损坏(越界写穿数据区——事发地定位)
# 内存哨兵(崩溃点漂移排查):reset 前检查存活块 header(抓越界写),reset 后检查 free list 完整性(抓链表损坏);任一损坏立即终止,把「随机崩溃漂移」变成「确定性定位到当前文件」。
# rc 语义(check_alive_blocks):1=块地址低于 usable,2=高于 usable+size,3=order 为负,4=order 超 max_order,5=块未对齐到 order 大小,6=块尾 canary 损坏(越界写穿数据区)
# rc 语义(self_check):1/2=越界,3=对齐错,4=free list 成环/超步数
chk_rc: int = mb.check_alive_blocks(mark1)
if chk_rc != 0:
VLogger.error(f"内存损坏(存活块 header, rc={chk_rc}): {file_path}", "Phase1")
# A/B 实验结论(已确认):禁用 reset_to 后 stdarg 仍崩 → 越界写是根因,
# 与回收/UAF 无关。恢复 reset_to,改用块尾 canary(check_alive_blocks 校验)
# 定位越界写事发地到具体文件。
# 越界写是崩溃根因(与回收/UAF 无关,已由 A/B 实验确认)——恢复 reset_to,改用块尾 canary(check_alive_blocks 校验)把越界写事发地定位到具体文件。
mb.reset_to(mark1)
sc_rc: int = mb.self_check()
if sc_rc != 0: