fix(phase1): 含 class 定义的 includes 模块强制翻译,修复增量编译跨模块类构造 find_struct_by_module 落空(fltk.Fl_Window 返回类型 fail-fast)

This commit is contained in:
TermiNexus committed 2026-09-01 21:06:31 +08:00
1 parent c7c37e1007
commit 178ea401b7
1 file changed
+410 -234
+410 -234
View File
@@ -1,5 +1,6 @@
import t, c import t, c
from stdint import * from stdint import *
import stdio
import string import string
import stdlib import stdlib
import memhub import memhub
@@ -29,6 +30,299 @@ import lib.Phase.Phase1 as Phase1
import lib.core.Gen.GenApi as GenApi 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 注册。
# ============================================================
def _InjectedCreatesCycle(entry: IncludesScanner.FileEntry | t.CPtr,
mod_name: str) -> int:
"""注入 mod_name 到当前模块前检查是否成环,返回 1=成环需跳过注入"""
if entry is None or entry.RelPath is None or mod_name is None:
return 0
inj_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
if inj_sha1 is None:
return 0
inj_deps: str = StubMergerStore.LookupIncludesDeps(inj_sha1)
if inj_deps is None:
return 0
rp: str = entry.RelPath
# 当前模块名 = RelPath 基名(最后 '/' 或 '\\' 之后)去扩展名
base: str = string.strrchr(rp, '/')
if base is None:
base = string.strrchr(rp, '\\')
if base is not None:
base = base + 1
else:
base = rp
dot: str = string.strchr(base, '.')
nm_buf: bytes = stdlib.malloc(128)
if nm_buf is None:
return 0
if dot is not None:
nm_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot) - t.CUInt64T(base))
if nm_len > 127:
nm_len = 127
string.strncpy(nm_buf, base, nm_len)
nm_buf[nm_len] = '\0'
else:
string.strncpy(nm_buf, base, 127)
nm_buf[127] = '\0'
nm_len2: t.CSizeT = string.strlen(nm_buf)
d_len: t.CSizeT = string.strlen(inj_deps)
dp: t.CSizeT = 0
while dp < d_len:
ts: t.CSizeT = dp
while dp < d_len and inj_deps[dp] != ' ':
dp += 1
tl: t.CSizeT = dp - ts
if dp < d_len:
dp += 1
if tl > 0 and tl == nm_len2 and string.strncmp(inj_deps + ts, nm_buf, tl) == 0:
stdlib.free(nm_buf)
return 1
stdlib.free(nm_buf)
return 0
def _DepsContainsName(deps: str, name: str) -> int:
"""deps 字符串(空格分隔模块名)是否含 name token(词边界精确匹配)"""
if deps is None or name is None:
return 0
nl: t.CSizeT = string.strlen(name)
dl: t.CSizeT = string.strlen(deps)
dp: t.CSizeT = 0
while dp < dl:
ts: t.CSizeT = dp
while dp < dl and deps[dp] != ' ':
dp += 1
tl: t.CSizeT = dp - ts
if dp < dl:
dp += 1
if tl == nl and string.strncmp(deps + ts, name, nl) == 0:
return 1
return 0
def _DepsRemoveName(deps: str, name: str) -> int:
"""从 deps 字符串移除 name token(原地重建,返回 1=有改动)"""
if deps is None or name is None:
return 0
nl: t.CSizeT = string.strlen(name)
dl: t.CSizeT = string.strlen(deps)
buf: bytes = stdlib.malloc(StubMerger.MAX_DEPS_STR_LEN)
if buf is None:
return 0
buf[0] = '\0'
bl: t.CSizeT = 0
dp: t.CSizeT = 0
while dp < dl:
ts: t.CSizeT = dp
while dp < dl and deps[dp] != ' ':
dp += 1
tl: t.CSizeT = dp - ts
if dp < dl:
dp += 1
if tl == 0:
continue
if tl == nl and string.strncmp(deps + ts, name, nl) == 0:
continue
if bl > 0:
buf[bl] = ' '
bl += 1
string.strncpy(buf + bl, deps + ts, tl)
bl += tl
buf[bl] = '\0'
string.strcpy(deps, buf)
stdlib.free(buf)
return 1
def _RewriteDepsTxt(temp_dir: str, sha1: str, deps_str: str) -> None:
"""重写 {temp_dir}/{sha1前2位}/{sha1}.deps.txt(内存破环后同步磁盘)"""
if temp_dir is None or sha1 is None or deps_str is None:
return
td_len: t.CSizeT = string.strlen(temp_dir)
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)
if not df.closed:
dl: t.CSizeT = string.strlen(deps_str)
if dl > 0:
df.write(deps_str, dl)
df.close()
stdlib.free(dpath)
def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
"""扫描后统一清理 _list/_dict 注入环边。
Phase1a-pre 全量扫描用 FindFirstFileA(文件系统序),注入 _list/_dict 时
被注入模块(_list/_dict)自身的 deps 未必已注册 → _InjectedCreatesCycle
内联检查可能漏判(如 _list 显式 import memhub,而 memhub 的 list[str]
注入 _list 未被拦截)→ memhub↔_list 2-环 → Phase A 永不收敛。此处扫描
结束后全量检查:凡某模块 M 的 deps 含 _list/_dict,且 _list/_dict 的 deps
含 M → 从 M 的 deps 移除注入边(list 类仍由 Phase1b 经闭包注册)。
内存破环后同步重写 {temp_dir}/deps.txt,保证磁盘与内存一致(Phase2 依赖
图诊断回退读 deps.txt)。
"""
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None:
return 0
cnt: t.CSizeT = StubMerger._includes_deps_count
# [CYCBREAK] 临时诊断:破环后处理是否执行、命中哪些模块(用后移除)
stdio.printf("[CYCBREAK] deps entries=%lu _list_sha1=%s\n",
t.CUInt64T(cnt),
StubMergerStore.LookupSha1ByModName("_list") if StubMergerStore.LookupSha1ByModName("_list") is not None else "none")
ci: t.CSizeT
for ci in range(cnt):
sha1_m: str = StubMerger._includes_deps_sha1 + t.CSizeT(ci) * 17
deps_m: str = StubMerger._includes_deps_str + t.CSizeT(ci) * StubMerger.MAX_DEPS_STR_LEN
if sha1_m is None or deps_m is None:
continue
if string.strstr(deps_m, "_list") is None and string.strstr(deps_m, "_dict") is None:
continue
# 该模块名(RelPath 基名去扩展名)
rel_m: str = StubMergerStore.LookupSha1RelPath(sha1_m)
if rel_m is None:
continue
base_m: str = string.strrchr(rel_m, '/')
if base_m is None:
base_m = string.strrchr(rel_m, '\\')
if base_m is not None:
base_m = base_m + 1
else:
base_m = rel_m
dot_m: str = string.strchr(base_m, '.')
if dot_m is not None:
nm_len_m: t.CSizeT = t.CSizeT(t.CUInt64T(dot_m) - t.CUInt64T(base_m))
nm_m: bytes = stdlib.malloc(nm_len_m + 1)
if nm_m is None:
continue
string.strncpy(nm_m, base_m, nm_len_m)
nm_m[nm_len_m] = '\0'
else:
nm_m: bytes = stdlib.malloc(129)
if nm_m is None:
continue
string.strncpy(nm_m, base_m, 128)
nm_m[128] = '\0'
# _list ↔ M 环
if string.strstr(deps_m, "_list") is not None:
lst_sha1: str = StubMergerStore.LookupSha1ByModName("_list")
if lst_sha1 is not None:
lst_deps: str = StubMergerStore.LookupIncludesDeps(lst_sha1)
if lst_deps is not None and _DepsContainsName(lst_deps, nm_m) != 0:
if _DepsRemoveName(deps_m, "_list") != 0:
# 内存破环后同步重写 deps.txt(诊断回退路径读文件)
_RewriteDepsTxt(temp_dir, sha1_m, deps_m)
# [CYCBREAK] 临时诊断:实际移除的环边(用后移除)
stdio.printf("[CYCBREAK] removed _list from %s deps\n", nm_m)
else:
stdio.printf("[CYCBREAK] %s has _list but _list deps no-match (lst_deps=%s)\n",
nm_m, lst_deps if lst_deps is not None else "none")
# _dict ↔ M 环
if string.strstr(deps_m, "_dict") is not None:
dct_sha1: str = StubMergerStore.LookupSha1ByModName("_dict")
if dct_sha1 is not None:
dct_deps: str = StubMergerStore.LookupIncludesDeps(dct_sha1)
if dct_deps is not None and _DepsContainsName(dct_deps, nm_m) != 0:
if _DepsRemoveName(deps_m, "_dict") != 0:
_RewriteDepsTxt(temp_dir, sha1_m, deps_m)
stdlib.free(nm_m)
return 0
# ============================================================
# _BuildReachableResult - 从 Sha1Store 构建可达 FileEntry 列表
#
# 依赖图按需扫描(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
# ============================================================
def _BuildReachableResult(mb: memhub.MemBuddy | t.CPtr,
includes_dir: str) -> IncludesScanner.ScanResult | t.CPtr:
"""从 Sha1Store(仅可达 includes + App)构建可达 FileEntry 列表"""
if mb is None:
return None
store_count: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count)
if store_count <= 0 or StubMerger._sha1_store_arr is None:
return None
result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.create_scan_result(mb)
if result is None:
return None
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
i: t.CSizeT
for i in range(store_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
if StubMerger._sha1_store_arr[sidx] == '\0':
continue
# 仅 includes 条目(incdir 非空);App 条目不进入 result
iidx: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_INC_DIR_LEN
if StubMerger._sha1_store_incdir[iidx] == '\0':
continue
eaddr: t.CUInt64T = t.CUInt64T(result.Entries) + t.CUInt64T(result.Count) * entry_size
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(eaddr, t.CPtr))
if entry is None:
continue
# SHA1 深拷贝(store 槽 17 字节)
sha1_buf: bytes = stdlib.malloc(17)
if sha1_buf is None:
continue
string.strcpy(sha1_buf, StubMerger._sha1_store_arr + sidx)
entry.Sha1 = sha1_buf
# RelPath / IncDir 深拷贝
ridx: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_REL_PATH_LEN
rel_len: t.CSizeT = string.strlen(StubMerger._sha1_store_rel + ridx)
inc_len: t.CSizeT = string.strlen(StubMerger._sha1_store_incdir + iidx)
rel_buf: bytes = stdlib.malloc(rel_len + 1)
inc_buf: bytes = stdlib.malloc(inc_len + 1)
if rel_buf is None or inc_buf is None:
if rel_buf is not None:
stdlib.free(rel_buf)
if inc_buf is not None:
stdlib.free(inc_buf)
stdlib.free(sha1_buf)
continue
string.strcpy(rel_buf, StubMerger._sha1_store_rel + ridx)
string.strcpy(inc_buf, StubMerger._sha1_store_incdir + iidx)
entry.RelPath = rel_buf
entry.IncDir = inc_buf
# 绝对路径 = inc_dir + '/' + rel_path(与 _TryResolveModuleFile 拼接一致)
abs_buf: bytes = stdlib.malloc(inc_len + 1 + rel_len + 1)
if abs_buf is None:
stdlib.free(sha1_buf)
stdlib.free(rel_buf)
stdlib.free(inc_buf)
continue
string.strcpy(abs_buf, inc_buf)
abs_buf[inc_len] = '/'
string.strcpy(abs_buf + inc_len + 1, rel_buf)
entry.Path = abs_buf
# 模块名(store 登记时保存的绝对模块名)
midx: t.CSizeT = t.CSizeT(i) * 64
mod_len: t.CSizeT = string.strlen(StubMerger._sha1_store_mod + midx)
if mod_len > 0:
mod_buf: bytes = stdlib.malloc(mod_len + 1)
if mod_buf is not None:
string.strcpy(mod_buf, StubMerger._sha1_store_mod + midx)
entry.ModuleName = mod_buf
result.Count += 1
return result
# ============================================================ # ============================================================
# Phase1Main - Phase1 拆分(阶段 E):Phase1Main # Phase1Main - Phase1 拆分(阶段 E):Phase1Main
# #
@@ -71,212 +365,19 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
log.banner("Phase1: 扫描 includes(按需翻译)") log.banner("Phase1: 扫描 includes(按需翻译)")
# 扫描 includes 目录(含每文件 SHA1 计算计时) # 初始化 AST 表(只需一次):必须在依赖图 BFS 扫描之前调用。
t_scan0: t.CUInt64T = w32.win32base.GetTickCount64() # _init_tables 懒分配关键字/操作符表(全局 _kw_head 等),若首次触发
result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir) # 发生在 BFS 的某个文件级 mark 之后,reset_to 会回收表节点 → 全局
t_scan1: t.CUInt64T = w32.win32base.GetTickCount64() # 悬空 → 后续 tokenize 的 _kw_lookup strcmp 访问 0x4 崩溃(1.log ASan)。
VLogger.info(f"扫描 includes 总耗时: {t_scan1 - t_scan0} ms ({result.Count if result is not None else 0} 文件, 含 SHA1 计算)", "Phase1")
if result is None:
VLogger.error("扫描失败", "Phase1")
return 1
# 从扫描结果直接构建 SHA1 集合(不依赖 _sha1_map.txt 文件)
# Phase1 作为编译器自身,应从内存中的扫描结果获取 SHA1,避免外部文件依赖
sha1_set: str = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17)
if sha1_set is None:
VLogger.error("sha1_set 分配失败", "Phase1")
return 1
string.memset(sha1_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
set_count: int = 0
p1_entry_size_init: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
for se_i in range(result.Count):
if set_count >= StubMerger.MAX_INCLUDES_SHA1:
break
se_addr: t.CUInt64T = t.CUInt64T(result.Entries) + se_i * p1_entry_size_init
se_ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(se_addr, t.CPtr))
if se_ent is None or se_ent.Sha1 is None:
continue
string.strcpy(sha1_set + set_count * 17, se_ent.Sha1)
set_count += 1
# 先填充全局 SHA1 映射存储器(即使 set_count==0 也填充,确保 Phase2 能获取 includes 条目)
# 注意:PopulateSha1MapStore 会先 free 旧存储器再重新分配
StubMergerStore.PopulateSha1MapStore(result)
# 写入 _sha1_map.txt(人类可读输出,程序内部不读取;机器分析使用 PopulateSha1MapStore 内存存储器)
StubMergerCollect.WriteIncludesSha1Map(mb, temp_dir, result, None, 0)
if set_count <= 0:
VLogger.warning("扫描结果无有效 SHA1,跳过 Phase1(项目可能无 includes 依赖)", "Phase1")
stdlib.free(sha1_set)
return 0
# 模块 SHA1 映射已由 PopulateSha1MapStore 填充到 StubMerger 全局存储器,
# HandlesExprCall._lookup_module_sha1 直接查全局存储器,不再需要独立副本。
# 初始化 AST 表(只需一次)
ast._init_tables(mb) ast._init_tables(mb)
# FileEntry 结构体大小(Phase 1a 和 1b 共用) # 依赖图按需扫描(App 优先模型):
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() # 1) 先扫 App 源文件(Config.SourceDir)的 import 依赖 → 初始队列
# 2) BFS 沿队列按文件系统解析 includes 文件,只对可达 includes
# ============================================================ # 读文件+算 SHA1+解析 import,补充依赖图并登记 Sha1Store/
# Phase 1a-pre: 扫描所有 includes 文件的 import 依赖 # deps 存储器/写 deps.txt
# # 3) 未引用的 includes 从不读取(根本不扫描)
# 只处理 Import/ImportFrom 语句,跳过 ClassDef/FunctionDef 等, t_scan0: t.CUInt64T = w32.win32base.GetTickCount64()
# 不调用 resolve_annotation_type,避免 list[...] 等不支持的语法触发 crash。
# 生成 .deps.txt 供依赖图按需翻译使用。
# ============================================================
p1a_registered: int = 0
p1a_skipped: int = 0
p1a_failed: int = 0
# 1a-pre 阶段总耗时起点(GetTickCount64,ms;对齐 Phase2 计时插桩)
t_pre0: t.CUInt64T = w32.win32base.GetTickCount64()
for i in range(result.Count):
# 文件级 mark:import 依赖收集完(deps 写盘 + PopulateIncludesDeps
# 内部 stdlib 深拷贝)后立即回收本文件 AST——98 includes 全量 parse
# 的 AST 若驻留到 mark1 → 1GB 主池耗尽 OOM 根因之一
scan_mark_a: t.CSizeT = mb.mark()
entry_addr_a: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry_a: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_a, t.CPtr))
if entry_a is None:
p1a_failed += 1
continue
sha1_a: str = entry_a.Sha1
if sha1_a is None:
p1a_failed += 1
continue
if StubMergerCollect._is_in_sha1_set(sha1_a, sha1_set, set_count) == 0:
p1a_skipped += 1
continue
# 读取文件内容
file_path_a: str = entry_a.Path
f_a: fileio.File | t.CPtr = fileio.File(file_path_a, fileio.MODE.R)
if f_a.closed:
p1a_failed += 1
continue
src_buf_a: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf_a is None:
f_a.close()
p1a_failed += 1
continue
bytes_read_a: LONG = f_a.read_all(src_buf_a, Phase1.SRC_BUF_SIZE)
f_a.close()
if bytes_read_a <= 0:
stdlib.free(src_buf_a)
p1a_failed += 1
continue
if bytes_read_a < Phase1.SRC_BUF_SIZE:
src_buf_a[bytes_read_a] = 0
else:
src_buf_a[Phase1.SRC_BUF_SIZE - 1] = 0
# 解析 AST
lx_a: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if lx_a is None:
stdlib.free(src_buf_a)
p1a_failed += 1
continue
ast._lexer_init(lx_a, src_buf_a, mb)
tokens_a: ast.Token | t.CPtr = ast.tokenize(lx_a)
tree_a: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens_a)
if tree_a is None:
stdlib.free(src_buf_a)
p1a_failed += 1
continue
# 创建 Translator,设置 _declare_only=2(只扫描 import,跳过 ClassDef)
tr_a: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
if tr_a is None:
stdlib.free(src_buf_a)
p1a_failed += 1
continue
tr_a.ModuleSha1 = sha1_a
tr_a._declare_only = 2
tr_a.CurrentPackage = GenApi.compute_package_from_relpath(mb, entry_a.RelPath)
GenApi.set_current_file_type(file_path_a)
GenApi.set_current_file_imports(file_path_a)
GenApi.set_current_module_sha1(sha1_a)
GenApi.clear_cdefine_constants()
GenApi.clear_ctypedefs()
GenApi.reset_visible_structs(mb, 0)
ret_a: int = tr_a.translate(tree_a)
if ret_a != 0:
p1a_failed += 1
else:
p1a_registered += 1
# 容器模板按需纳入依赖:includes 使用 list/dict 泛型/构造/字面量
# (如 base.vp 的 list[AST | CPtr])时追加 _list/_dict 到
# _imported_modules → 进 deps → _BuildReachableSha1Set 的可达集合
# 自动包含(Phase1 与 Phase2 都受益),否则泛型模板未注册 →
# "泛型类 'list' 未找到模板"。对齐 App 层 _inject_auto_container_visibility。
cl_box: t.CPtr = stdlib.malloc(4)
cd_box: t.CPtr = stdlib.malloc(4)
if cl_box is not None and cd_box is not None:
cl_box[0] = 0
cd_box[0] = 0
Phase1Scan._ScanTreeForContainers(tree_a, cl_box, cd_box)
if cl_box[0] != 0:
if tr_a._imported_modules is None or \
GenApi.is_module_imported(tr_a._imported_modules, "_list") == 0:
tr_a._imported_modules = GenApi.add_imported_module(
mb, tr_a._imported_modules, "_list")
if cd_box[0] != 0:
if tr_a._imported_modules is None or \
GenApi.is_module_imported(tr_a._imported_modules, "_dict") == 0:
tr_a._imported_modules = GenApi.add_imported_module(
mb, tr_a._imported_modules, "_dict")
if cl_box is not None:
stdlib.free(cl_box)
if cd_box is not None:
stdlib.free(cd_box)
# 生成 .deps.txt(记录依赖模块名,供依赖图按需翻译使用)
# 同时填充内存依赖存储器(PopulateIncludesDeps),供 _BuildReachableSha1Set
# 直接从内存读取,不依赖 .deps.txt 过程文件
td_len_a: t.CSizeT = string.strlen(temp_dir)
deps_path_a: str = StubMergerCollect._sliced_path(temp_dir, td_len_a, sha1_a, "deps.txt")
if deps_path_a is not None:
df_a: fileio.File | t.CPtr = fileio.File(deps_path_a, fileio.MODE.W)
if not df_a.closed:
if tr_a._imported_modules is not None:
dl_a: t.CSizeT = string.strlen(tr_a._imported_modules)
df_a.write(tr_a._imported_modules, dl_a)
df_a.close()
stdlib.free(deps_path_a)
# 填充内存依赖存储器(供 _BuildReachableSha1Set 使用,不读 .deps.txt)
if tr_a._imported_modules is not None:
StubMergerStore.PopulateIncludesDeps(sha1_a, tr_a._imported_modules)
# 释放 Translator 的 C malloc 资源
if tr_a._global_names is not None:
stdlib.free(tr_a._global_names)
if tr_a._nonlocal_names is not None:
stdlib.free(tr_a._nonlocal_names)
stdlib.free(src_buf_a)
# 回收本文件 AST/lexer/token(deps 已写盘、依赖存储器已深拷贝)
mb.reset_to(scan_mark_a)
# 1a-pre 阶段总耗时(全部 includes 文件读+parse+import 依赖扫描)
t_pre1: t.CUInt64T = w32.win32base.GetTickCount64()
VLogger.info(f"1a-pre 总耗时: {t_pre1 - t_pre0} ms ({result.Count} 文件扫描 import 依赖)", "Phase1")
if p1a_failed > 0:
VLogger.error(f"Phase 1a-pre 有 {p1a_failed} 个文件扫描失败", "Phase1")
return 1
# ============================================================
# 依赖图按需翻译:构建可达 SHA1 集合
#
# 从 Config.SourceDir 的源文件开始,解析 import 语句,递归收集
# 可达的 includes 文件 SHA1。Phase 1b 只翻译可达集合中的文件,
# 避免翻译不需要的 includes(如 Test 不依赖 llvmlite,则不翻译)。
# 如果 Config.SourceDir 不可用(仅运行 Phase1),回退到 sha1_set。
# ============================================================
reachable_set: str = None reachable_set: str = None
reachable_count: int = 0 reachable_count: int = 0
use_reachable: int = 0 use_reachable: int = 0
@@ -284,22 +385,52 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
reachable_set = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17) reachable_set = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17)
if reachable_set is not None: if reachable_set is not None:
string.memset(reachable_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17) string.memset(reachable_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
reachable_count = StubMergerBuild._BuildReachableSha1Set(mb, Config.SourceDir, temp_dir, reachable_set) reachable_count = StubMergerBuild._BuildReachableSha1Set(
mb, Config.SourceDir, temp_dir, reachable_set, None, includes_dir)
if reachable_count < 0: if reachable_count < 0:
VLogger.error("可达集合构建失败,终止", "Phase1") VLogger.error("可达集合构建失败(依赖图扫描失败),终止编译", "Phase1")
return 1 return 1
if reachable_count > 0: if reachable_count <= 0:
use_reachable = 1 VLogger.warning("App 无可达 includes 依赖,跳过 Phase1", "Phase1")
# 用可达集合重新生成 _sha1_map.txt(按图求索的最终产物)
# Phase B+ 只遍历这些条目,避免编译不需要的 includes(如 asm.vp)
StubMergerCollect.WriteIncludesSha1Map(mb, temp_dir, result, reachable_set, reachable_count)
# 重新加载 sha1_set,使后续 Phase 1a-pre/1a/1b 的过滤也使用可达集合
string.memset(sha1_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
set_count = StubMergerCollect._load_includes_sha1_set(mb, temp_dir, sha1_set)
else:
VLogger.warning("可达集合为空,回退全量扫描", "Phase1")
stdlib.free(reachable_set) stdlib.free(reachable_set)
reachable_set = None reachable_set = None
return 0
use_reachable = 1
else:
VLogger.error("reachable_set 分配失败,终止编译", "Phase1")
return 1
else:
VLogger.warning("SourceDir 不可用,无法构建依赖图,跳过 Phase1", "Phase1")
return 0
t_scan1: t.CUInt64T = w32.win32base.GetTickCount64()
VLogger.info(f"依赖图扫描总耗时: {t_scan1 - t_scan0} ms(可达 {reachable_count} 个 includes)", "Phase1")
# 从 Sha1Store(仅可达 includes + App)构造可达 FileEntry 列表:
# 后续 1a/1b-pre/1b 直接遍历 result(incdir 非空条目=includes),
# use_reachable 过滤幂等保留(result 即可达子集)
result: IncludesScanner.ScanResult | t.CPtr = _BuildReachableResult(mb, includes_dir)
if result is None:
VLogger.error("可达 FileEntry 列表构建失败,终止编译", "Phase1")
return 1
VLogger.info(f"可达 includes 条目: {result.Count}", "Phase1")
# 扫描后统一清理 _list/_dict 注入环边(_list↔memhub 等 2-环)。
# 依赖图按需扫描顺序不可靠,内联检查可能漏判,此处全量清理保证
# 依赖图无注入环,Phase A 多遍可收敛。破环后同步重写 deps.txt。
_BreakContainerCyclesInDeps(temp_dir)
# 写 _sha1_map.txt(仅可达 includes,人类可读输出;机器分析使用内存存储器)
StubMergerCollect.WriteIncludesSha1Map(mb, temp_dir, result, reachable_set, reachable_count)
# 兼容旧引用:sha1_set/set_count 对齐可达集合(use_reachable=1 时不被使用)
sha1_set: str = reachable_set
set_count: int = reachable_count
# 模块 SHA1 映射已由依赖图扫描登记到 StubMerger 全局存储器(仅可达 includes + App),
# HandlesExprCall._lookup_module_sha1 直接查全局存储器。
# FileEntry 结构体大小(Phase 1a 和 1b 共用)
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
# ============================================================ # ============================================================
# Phase 1a: 注册可达 includes 文件的 struct/enum/union # Phase 1a: 注册可达 includes 文件的 struct/enum/union
@@ -703,26 +834,71 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
continue continue
# 检查 stub 是否已存在(按需翻译:跳过已存在的) # 检查 stub 是否已存在(按需翻译:跳过已存在的)
sf: fileio.File | t.CPtr = fileio.File(stub_path, fileio.MODE.R) # 例外:含 class 定义的模块必须翻译——类注册(register_struct)
if not sf.closed: # 只在翻译时发生;增量编译时 stub/text 已存在被跳过 → 跨模块类构造
sf.close() # find_struct_by_module 落空 → 返回类型 fail-fast
# 检查 text.ll 是否也存在(虚表扫描需要 text.ll) # (fltk.Fl_Window(...) → "4049e14d73c7e091.Fl_Window 无法精确获取",
text_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "text.ll") # 仅 --clean 全量翻译时可用)。扫描 1b-pre 生成的 vpi 是否有
if text_path is not None: # "class " 行(vpi 为解析后存根,精确无注释/字符串误判)。
tf: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R) force_translate: int = 0
if not tf.closed: vpi_path_1b: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi")
tf.close() if vpi_path_1b is not None:
stdlib.free(text_path) vf_1b: fileio.File | t.CPtr = fileio.File(vpi_path_1b, fileio.MODE.R)
stdlib.free(stub_path) if not vf_1b.closed:
continue vbuf_1b: bytes = stdlib.malloc(1048576)
stdlib.free(text_path) if vbuf_1b is not None:
# text.ll 不存在,需要重新翻译 vrd_1b: LONG = vf_1b.read_all(vbuf_1b, 1048576)
vf_1b.close()
if vrd_1b > 0:
if vrd_1b < 1048576:
vbuf_1b[vrd_1b] = 0
else:
vbuf_1b[1048576 - 1] = 0
vtotal_1b: t.CSizeT = string.strlen(vbuf_1b)
vpos_1b: t.CSizeT = 0
while vpos_1b < vtotal_1b:
vls_1b: t.CSizeT = vpos_1b
while vpos_1b < vtotal_1b and vbuf_1b[vpos_1b] != '\n':
vpos_1b += 1
vll_1b: t.CSizeT = vpos_1b - vls_1b
if vpos_1b < vtotal_1b:
vpos_1b += 1
vi2_1b: t.CSizeT = vls_1b
while vi2_1b < vls_1b + vll_1b and vbuf_1b[vi2_1b] == ' ':
vi2_1b += 1
if vll_1b >= 6 and string.strncmp(vbuf_1b + vi2_1b, "class ", 6) == 0:
force_translate = 1
break
stdlib.free(vbuf_1b)
else:
vf_1b.close()
stdlib.free(vpi_path_1b)
if force_translate == 1:
# 含 class:强制翻译(覆盖 stub/text,重新注册类)
rp: str = entry.RelPath rp: str = entry.RelPath
VLogger.info(f"text.ll 不存在,重新翻译: {rp}(sha1={sha1})", "Phase1") VLogger.info(f"含 class 定义,强制翻译: {rp}(sha1={sha1})", "Phase1")
else: else:
# stub 不存在,需要翻译 # 检查 stub 是否已存在(按需翻译:跳过已存在的)
rp: str = entry.RelPath sf: fileio.File | t.CPtr = fileio.File(stub_path, fileio.MODE.R)
VLogger.info(f"翻译: {rp}(sha1={sha1})", "Phase1") 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)
if not tf.closed:
tf.close()
stdlib.free(text_path)
stdlib.free(stub_path)
continue
stdlib.free(text_path)
# text.ll 不存在,需要重新翻译
rp: str = entry.RelPath
VLogger.info(f"text.ll 不存在,重新翻译: {rp}(sha1={sha1})", "Phase1")
else:
# stub 不存在,需要翻译
rp: str = entry.RelPath
VLogger.info(f"翻译: {rp}(sha1={sha1})", "Phase1")
stdlib.free(stub_path) stdlib.free(stub_path)