import t, c from stdint import * import memhub import string import stdlib import llvmlite import w32.win32file as win32file import w32.win32base as win32base import w32.fileio as fileio import viperlib import sys import ast import lib.core.VLogger as VLogger import lib.core.IncludesScanner as IncludesScanner import lib.core.Handles.HandlesTranslator as HandlesTranslator import lib.Phase.BuildPipeline as BuildPipeline import lib.Stub.StubGen.VpiReader as VpiReader import lib.Projectrans.Utils as Utils import lib.Projectrans.Config as Config import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect import lib.Stub.StubMerger.StubMergerStore as StubMergerStore import lib.Stub.StubMerger.StubMergerIR as StubMergerIR import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild import lib.Stub.StubMerger as StubMerger # ============================================================ # StubMergerTopo - StubMerger 拆分(阶段 E):StubMergerTopo # # 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留 # 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经 # StubMerger._xxx 访问(main.vp 先例)。 # ============================================================ # ============================================================ # 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 # 清零 indegree/queue/rel_idx(malloc 返回未初始化内存,Kahn 依赖入度初值 0) zi: int = 0 for zi in range(store_count + 1): indegree[zi] = 0 queue[zi] = 0 rel_idx[zi] = 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 StubMergerCollect._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 # 诊断:可达 store 索引数(rel_count=0 → topo_count=0 → PhaseB+ 循环不执行) fb_rel: str = VLogger.fmt_buf() if fb_rel is not None: viperlib.snprintf(fb_rel, 1024, "诊断 topo rel_count=%d", rel_count) VLogger.debug(fb_rel, "StubMergerTopo") # 计算入度:m 在可达集合内的直接依赖数 i_r: int for i_r in range(rel_count): si: int = rel_idx[i_r] deps_si: str = StubMergerStore.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 = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf) stdlib.free(dname_buf) if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0: # 中间 int 变量累加:绕过联合类型(CSizeT|CPtr)+ 整数的指针算术编译 bug indg: int = indegree[si] indg = indg + 1 indegree[si] = indg # Kahn:入度为 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 # Kahn 主循环必须用 while(不能用 for range):循环体内会动态入队 # (queue[q_tail]=si3; q_tail+=1),for range 上界只求值一次, # 动态入队的模块永远不会被处理且兜底(indegree>0)也捞不回 → 拓扑漏项 q_head: int = 0 while q_head < q_tail: m: int = queue[q_head] 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 = StubMergerStore.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: # 中间 int 变量递减:绕过联合类型指针算术编译 bug indg3: int = indegree[si3] indg3 = indg3 - 1 indegree[si3] = indg3 if indegree[si3] == 0: queue[q_tail] = si3 q_tail += 1 q_head += 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