fix(phaseb+): includes 按依赖拓扑序编译,修复跨模块 vtable 误杀
astaux.vp 继承 base.vp 的 AST 类时,vtable 槽位需 AST.accept 的 TypeStr 已注册到全局表(完整翻译产物)。Phase B+ 原先按 store 扫描 顺序编译,astaux 先于 base 编译 → 继承方法找不到实现被 fail-fast 误杀。 新增 StubMerger.BuildTopoSortedIndices(Kahn 逆拓扑,被依赖者先), Phase B+ 循环按拓扑序编译可达 includes。
This commit is contained in:
1 parent
ad2b97a140
commit
7cc501cdef
2 files changed
+155
-9
No files matched your search
+22
-9
@@ -728,15 +728,24 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
|
||||
store_incdir_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreIncdirArrPtr()
|
||||
|
||||
for si_mi in range(store_count_mi):
|
||||
# 拓扑排序可达 includes:被依赖模块先完整翻译。跨模块 vtable 生成
|
||||
# 需要依赖模块的函数 TypeStr 已注册到全局表(如 astaux 继承 base 的 AST)
|
||||
topo_idx: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count_mi + 1)))
|
||||
topo_count: int = 0
|
||||
if topo_idx is None:
|
||||
VLogger.error("拓扑排序索引分配失败,终止编译", "PhaseB+")
|
||||
topo_count = 0
|
||||
else:
|
||||
topo_count = StubMerger.BuildTopoSortedIndices(
|
||||
store_count_mi, store_sha1_arr_mi, store_mod_arr_mi,
|
||||
reachable_set, reachable_count, topo_idx)
|
||||
|
||||
for si_mi in range(topo_count):
|
||||
# 当前条目(拓扑序索引,仅含可达模块)
|
||||
ti: int = topo_idx[si_mi]
|
||||
# 获取当前条目的 SHA1 和 rel_path
|
||||
inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(si_mi) * 17
|
||||
inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_REL_PATH_LEN
|
||||
# 按需编译过滤:只处理 reachable_set 中的 includes
|
||||
# 永不回退:reachable_count > 0 时严格按可达集合过滤
|
||||
if reachable_count > 0:
|
||||
if StubMerger._is_in_sha1_set(inc_sha1_mi, reachable_set, reachable_count) == 0:
|
||||
continue
|
||||
inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(ti) * 17
|
||||
inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(ti) * StubMerger.MAX_REL_PATH_LEN
|
||||
# 检查 .obj 是否已存在于 includes.binary(切片子目录)
|
||||
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
|
||||
check_pat_mi: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj")
|
||||
@@ -757,7 +766,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
# .obj 不存在,需要编译
|
||||
# 构造源文件路径: {所属 includes 根目录}/{rel_path}(多目录逐条取 IncDir,空串回退 includes_dir)
|
||||
rel_path_len_mi: t.CSizeT = string.strlen(inc_rel_mi)
|
||||
incdir_mi: str = store_incdir_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_INC_DIR_LEN
|
||||
incdir_mi: str = store_incdir_arr_mi + t.CSizeT(ti) * StubMerger.MAX_INC_DIR_LEN
|
||||
if incdir_mi is None or incdir_mi[0] == '\0':
|
||||
incdir_mi = includes_dir
|
||||
inc_dir_len_mi: t.CSizeT = string.strlen(incdir_mi)
|
||||
@@ -940,6 +949,10 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
stdlib.free(src_fp_mi)
|
||||
|
||||
# 释放拓扑排序索引
|
||||
if topo_idx is not None:
|
||||
stdlib.free(topo_idx)
|
||||
|
||||
fb: str = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "编译缺失 includes: %d 个", inc_compiled)
|
||||
|
||||
@@ -653,6 +653,139 @@ def LookupIncludesDeps(sha1: str) -> str:
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先)
|
||||
#
|
||||
# 背景:跨模块 vtable 生成要求被依赖模块先完整翻译(如 astaux.vp 继承
|
||||
# base.vp 的 AST,vtable 槽位需 AST.accept 的 TypeStr 已注册到全局表)。
|
||||
# Phase B+ 原先按 store 扫描顺序编译,依赖顺序不保证 → fail-fast 暴露。
|
||||
#
|
||||
# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中)。
|
||||
# indegree[m] = m 在可达集合内的直接依赖数;
|
||||
# 入度为 0(不依赖任何可达模块)者先输出,输出后对依赖它的模块
|
||||
# 减入度,入度归 0 者入队。结果即"被依赖者先编译"。
|
||||
# reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。
|
||||
#
|
||||
# Args:
|
||||
# store_count / store_sha1_arr / store_mod_arr: store 元数据
|
||||
# reachable_set / reachable_count: 可达集合(_BuildReachableSha1Set 输出)
|
||||
# sorted_idx: 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引
|
||||
#
|
||||
# Returns:
|
||||
# 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译)
|
||||
# ============================================================
|
||||
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
|
||||
store_mod_arr: bytes, reachable_set: bytes,
|
||||
reachable_count: int,
|
||||
sorted_idx: t.CSizeT | t.CPtr) -> int:
|
||||
"""对可达 includes 逆拓扑排序(被依赖者先),store 索引写入 sorted_idx"""
|
||||
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None:
|
||||
return 0
|
||||
indegree: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
queue: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
rel_idx: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
if indegree is None or queue is None or rel_idx is None:
|
||||
if indegree is not None:
|
||||
stdlib.free(indegree)
|
||||
if queue is not None:
|
||||
stdlib.free(queue)
|
||||
if rel_idx is not None:
|
||||
stdlib.free(rel_idx)
|
||||
return 0
|
||||
# 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为)
|
||||
rel_count: int = 0
|
||||
i_t: int
|
||||
if reachable_set is None or reachable_count <= 0:
|
||||
for i_t in range(store_count):
|
||||
rel_idx[rel_count] = i_t
|
||||
rel_count += 1
|
||||
else:
|
||||
for i_t in range(store_count):
|
||||
if _is_in_sha1_set(store_sha1_arr + t.CSizeT(i_t) * 17, reachable_set, reachable_count) != 0:
|
||||
rel_idx[rel_count] = i_t
|
||||
rel_count += 1
|
||||
# 计算入度:m 在可达集合内的直接依赖数
|
||||
i_r: int
|
||||
for i_r in range(rel_count):
|
||||
si: int = rel_idx[i_r]
|
||||
deps_si: str = LookupIncludesDeps(store_sha1_arr + t.CSizeT(si) * 17)
|
||||
if deps_si is None:
|
||||
continue
|
||||
dl_si: t.CSizeT = string.strlen(deps_si)
|
||||
dpos: t.CSizeT = 0
|
||||
while dpos < dl_si:
|
||||
dstart: t.CSizeT = dpos
|
||||
while dpos < dl_si and deps_si[dpos] != ' ':
|
||||
dpos += 1
|
||||
dlen: t.CSizeT = dpos - dstart
|
||||
if dpos < dl_si:
|
||||
dpos += 1
|
||||
if dlen == 0:
|
||||
continue
|
||||
dname_buf: bytes = stdlib.malloc(dlen + 1)
|
||||
if dname_buf is None:
|
||||
continue
|
||||
string.strncpy(dname_buf, deps_si + dstart, dlen)
|
||||
dname_buf[dlen] = '\0'
|
||||
dsha1: str = _FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf)
|
||||
stdlib.free(dname_buf)
|
||||
if dsha1 is not None and _is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
|
||||
indegree[si] = indegree[si] + 1
|
||||
# Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译
|
||||
q_head: int = 0
|
||||
q_tail: int = 0
|
||||
for i_r in range(rel_count):
|
||||
si2: int = rel_idx[i_r]
|
||||
if indegree[si2] == 0:
|
||||
queue[q_tail] = si2
|
||||
q_tail += 1
|
||||
out_count: int = 0
|
||||
while q_head < q_tail:
|
||||
m: int = queue[q_head]
|
||||
q_head += 1
|
||||
sorted_idx[out_count] = m
|
||||
out_count += 1
|
||||
# 对依赖 m 的可达模块减入度
|
||||
mod_m: str = store_mod_arr + t.CSizeT(m) * 64
|
||||
mod_m_len: t.CSizeT = string.strlen(mod_m)
|
||||
for i_r in range(rel_count):
|
||||
si3: int = rel_idx[i_r]
|
||||
if indegree[si3] == 0:
|
||||
continue
|
||||
deps_si3: str = LookupIncludesDeps(store_sha1_arr + t.CSizeT(si3) * 17)
|
||||
if deps_si3 is None:
|
||||
continue
|
||||
dl_si3: t.CSizeT = string.strlen(deps_si3)
|
||||
dpos3: t.CSizeT = 0
|
||||
found_dep: int = 0
|
||||
while dpos3 < dl_si3 and found_dep == 0:
|
||||
dstart3: t.CSizeT = dpos3
|
||||
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ':
|
||||
dpos3 += 1
|
||||
dlen3: t.CSizeT = dpos3 - dstart3
|
||||
if dpos3 < dl_si3:
|
||||
dpos3 += 1
|
||||
if dlen3 == 0:
|
||||
continue
|
||||
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0:
|
||||
found_dep = 1
|
||||
if found_dep == 1:
|
||||
indegree[si3] = indegree[si3] - 1
|
||||
if indegree[si3] == 0:
|
||||
queue[q_tail] = si3
|
||||
q_tail += 1
|
||||
# 有环时剩余条目按 store 顺序补齐(避免漏编译)
|
||||
for i_r in range(rel_count):
|
||||
si4: int = rel_idx[i_r]
|
||||
if indegree[si4] > 0:
|
||||
sorted_idx[out_count] = si4
|
||||
out_count += 1
|
||||
stdlib.free(indegree)
|
||||
stdlib.free(queue)
|
||||
stdlib.free(rel_idx)
|
||||
return out_count
|
||||
|
||||
|
||||
def GetSha1StoreArrPtr() -> bytes | t.CPtr:
|
||||
"""返回 SHA1 数组指针(每个条目 17 字节)"""
|
||||
return _sha1_store_arr
|
||||
|
||||
Reference in new issue
Block a user