refactor(tpv): 新语法 R10 单语句块并一行(7722 处 if/for/while 单语句体)

This commit is contained in:
TermiNexus committed 2026-10-05 03:10:55 +08:00
1 parent f923dc4d8d
commit 4a685d478f
69 files changed
+7723 -15445

No files matched your search

File diff suppressed because it is too large. Load diff
+98 -196
View File
@@ -39,31 +39,23 @@ import lib.core.Gen.DebugInfoGen as DebugInfoGen
def _InjectedCreatesCycle(entry: rawptr[IncludesScanner.FileEntry],
mod_name: str) -> int:
"""注入 mod_name 到当前模块前检查是否成环,返回 1=成环需跳过注入"""
if entry is None or entry.RelPath is None or mod_name is None:
return 0
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
if inj_sha1 is None: return 0
inj_deps: str = StubMergerStore.LookupIncludesDeps(inj_sha1)
if inj_deps is None:
return 0
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
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 nm_buf is None: return 0
if dot is not None:
nm_len: sizet = sizet(uint64(dot) - uint64(base))
if nm_len > 127:
nm_len = 127
if nm_len > 127: nm_len = 127
string.strncpy(nm_buf, base, nm_len)
nm_buf[nm_len] = '\0'
else:
@@ -74,11 +66,9 @@ def _InjectedCreatesCycle(entry: rawptr[IncludesScanner.FileEntry],
dp: sizet = 0
while dp < d_len:
ts: sizet = dp
while dp < d_len and inj_deps[dp] != ' ':
dp += 1
while dp < d_len and inj_deps[dp] != ' ': dp += 1
tl: sizet = dp - ts
if dp < d_len:
dp += 1
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
@@ -88,46 +78,36 @@ def _InjectedCreatesCycle(entry: rawptr[IncludesScanner.FileEntry],
def _DepsContainsName(deps: str, name: str) -> int:
"""deps 字符串(空格分隔模块名)是否含 name token(词边界精确匹配)"""
if deps is None or name is None:
return 0
if deps is None or name is None: return 0
nl: sizet = string.strlen(name)
dl: sizet = string.strlen(deps)
dp: sizet = 0
while dp < dl:
ts: sizet = dp
while dp < dl and deps[dp] != ' ':
dp += 1
while dp < dl and deps[dp] != ' ': dp += 1
tl: sizet = dp - ts
if dp < dl:
dp += 1
if tl == nl and string.strncmp(deps + ts, name, nl) == 0:
return 1
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
if deps is None or name is None: return 0
nl: sizet = string.strlen(name)
dl: sizet = string.strlen(deps)
buf: bytes = stdlib.malloc(StubMerger.MAX_DEPS_STR_LEN)
if buf is None:
return 0
if buf is None: return 0
buf[0] = '\0'
bl: sizet = 0
dp: sizet = 0
while dp < dl:
ts: sizet = dp
while dp < dl and deps[dp] != ' ':
dp += 1
while dp < dl and deps[dp] != ' ': dp += 1
tl: sizet = dp - ts
if dp < dl:
dp += 1
if tl == 0:
continue
if tl == nl and string.strncmp(deps + ts, name, nl) == 0:
continue
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
@@ -141,17 +121,14 @@ def _DepsRemoveName(deps: str, name: str) -> int:
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
if temp_dir is None or sha1 is None or deps_str is None: return
td_len: sizet = string.strlen(temp_dir)
dpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "deps.txt")
if dpath is None:
return
if dpath is None: return
df: rawptr[fileio.File] = fileio.File(dpath, fileio.MODE.W)
if not df.closed:
dl: sizet = string.strlen(deps_str)
if dl > 0:
df.write(deps_str, dl)
if dl > 0: df.write(deps_str, dl)
df.close()
stdlib.free(dpath)
@@ -168,40 +145,31 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
内存破环后同步重写 {temp_dir}/deps.txt,保证磁盘与内存一致(Phase2 依赖
图诊断回退读 deps.txt)。
"""
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None:
return 0
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: return 0
cnt: sizet = StubMerger._includes_deps_count
ci: sizet
for ci in range(cnt):
sha1_m: str = StubMerger._includes_deps_sha1 + sizet(ci) * 17
deps_m: str = StubMerger._includes_deps_str + sizet(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
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
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
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: sizet = sizet(uint64(dot_m) - uint64(base_m))
nm_m: bytes = stdlib.malloc(nm_len_m + 1)
if nm_m is None:
continue
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
if nm_m is None: continue
string.strncpy(nm_m, base_m, 128)
nm_m[128] = '\0'
# _list ↔ M 环
@@ -219,8 +187,7 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
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)
if _DepsRemoveName(deps_m, "_dict") != 0: _RewriteDepsTxt(temp_dir, sha1_m, deps_m)
stdlib.free(nm_m)
return 0
@@ -238,32 +205,25 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
def _BuildReachableResult(mb: memhub.MemBuddy*,
includes_dir: str) -> rawptr[IncludesScanner.ScanResult]:
"""从 Sha1Store(仅可达 includes + App)构建可达 FileEntry 列表"""
if mb is None:
return None
if mb is None: return None
store_count: sizet = sizet(StubMerger._sha1_store_count)
if store_count <= 0 or StubMerger._sha1_store_arr is None:
return None
if store_count <= 0 or StubMerger._sha1_store_arr is None: return None
result: rawptr[IncludesScanner.ScanResult] = IncludesScanner.create_scan_result(mb)
if result is None:
return None
if result is None: return None
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
i: sizet
for i in range(store_count):
sidx: sizet = sizet(i) * 17
if StubMerger._sha1_store_arr[sidx] == '\0':
continue
if StubMerger._sha1_store_arr[sidx] == '\0': continue
# 仅 includes 条目(incdir 非空);App 条目不进入 result
iidx: sizet = sizet(i) * StubMerger.MAX_INC_DIR_LEN
if StubMerger._sha1_store_incdir[iidx] == '\0':
continue
if StubMerger._sha1_store_incdir[iidx] == '\0': continue
eaddr: uint64 = uint64(result.Entries) + uint64(result.Count) * entry_size
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](eaddr)
if entry is None:
continue
if entry is None: continue
# SHA1 深拷贝(store 槽 17 字节)
sha1_buf: bytes = stdlib.malloc(17)
if sha1_buf is None:
continue
if sha1_buf is None: continue
string.strcpy(sha1_buf, StubMerger._sha1_store_arr + sidx)
entry.Sha1 = sha1_buf
# RelPath / IncDir 深拷贝
@@ -273,10 +233,8 @@ def _BuildReachableResult(mb: memhub.MemBuddy*,
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)
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)
@@ -419,10 +377,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
# free_bytes/free_count 无步数上限会死循环(打点自身卡死),故
# self_check 返回 4(成环)时跳过 dump_stats。
p1_sc: int = mb.self_check()
if p1_sc != 4:
mb.dump_stats("phase1-pre")
else:
VLogger.info("[取证] self_check=4(free list 成环),跳过 dump_stats 防死循环", "Phase1")
if p1_sc != 4: mb.dump_stats("phase1-pre")
else: VLogger.info("[取证] self_check=4(free list 成环),跳过 dump_stats 防死循环", "Phase1")
p1_mo: int = mb.max_order
if p1_mo < 0 or p1_mo > 32:
p1_mo = 32 # 钳制:max_order 被写坏时防本打点自身越界读 free_lists
@@ -465,33 +421,24 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
topo_count_box: t.CPtr = mb.alloc(4)
generic_file_indices: t.CPtr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
generic_file_count_box: t.CPtr = mb.alloc(4)
if generic_file_indices is not None:
string.memset(generic_file_indices, 0, 4 * Phase1.MAX_FILE_DEPS)
if generic_file_count_box is not None:
string.memset(generic_file_count_box, 0, 4)
if generic_file_indices is not None: string.memset(generic_file_indices, 0, 4 * Phase1.MAX_FILE_DEPS)
if generic_file_count_box is not None: 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 使用,有效性由其入口自身校验(NULL 即返回 -1 并终止),不并入此处终止逻辑。
if class_names_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败:class_names_buf 为 NULL,终止编译", "Phase1")
if parent_names_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败:parent_names_buf 为 NULL,终止编译", "Phase1")
if def_sha1s_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败:def_sha1s_buf 为 NULL,终止编译", "Phase1")
if class_names_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败:class_names_buf 为 NULL,终止编译", "Phase1")
if parent_names_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败:parent_names_buf 为 NULL,终止编译", "Phase1")
if def_sha1s_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败:def_sha1s_buf 为 NULL,终止编译", "Phase1")
string.memset(class_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
string.memset(parent_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
string.memset(def_sha1s_buf, 0, Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
if topo_order is not None:
string.memset(topo_order, 0, 4 * Phase1.MAX_FILE_DEPS)
if topo_count_box is not None:
string.memset(topo_count_box, 0, 4)
if generic_file_indices is not None:
string.memset(generic_file_indices, 0, 4 * Phase1.MAX_FILE_DEPS)
if generic_file_count_box is not None:
string.memset(generic_file_count_box, 0, 4)
if topo_order is not None: string.memset(topo_order, 0, 4 * Phase1.MAX_FILE_DEPS)
if topo_count_box is not None: string.memset(topo_count_box, 0, 4)
if generic_file_indices is not None: string.memset(generic_file_indices, 0, 4 * Phase1.MAX_FILE_DEPS)
if generic_file_count_box is not None: string.memset(generic_file_count_box, 0, 4)
class_count_scanned: int = Phase1Scan._ScanClassInheritance(
mb, result, reachable_set, reachable_count,
@@ -509,8 +456,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
temp_dir, td_len_ts_call)
if ret_topo == 0:
tc_ptr: t.CPtr = (int*)(topo_count_box)
if tc_ptr is not None:
topo_count = tc_ptr[0]
if tc_ptr is not None: topo_count = tc_ptr[0]
VLogger.info(f"拓扑排序: {class_count_scanned} 个类, {topo_count} 个文件按依赖顺序处理", "Phase1")
else:
VLogger.error("拓扑排序失败(类继承依赖无法解析),终止编译", "Phase1")
@@ -525,10 +471,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
# 非可达泛型文件(_list/_dict/linkedlist/mlirlite 等)的模板若在 Phase2(mark2 之后)才注册会被 reset 回收 → list[int]/dict[...] 特化失败。
# 故对全部 includes 轻量预注册,使模板表与节点都落在 mark1 之前(_PreRegisterAllGenericTemplates 内有 generic marker 文本预筛,不 parse 全部 AST,OOM 风险可控)。
full_scan_g: rawptr[IncludesScanner.ScanResult] = IncludesScanner.scan_includes(mb, includes_dir)
if full_scan_g is not None:
Phase1Scan._PreRegisterAllGenericTemplates(mb, full_scan_g)
else:
Phase1Scan._PreRegisterAllGenericTemplates(mb, result)
if full_scan_g is not None: Phase1Scan._PreRegisterAllGenericTemplates(mb, full_scan_g)
else: Phase1Scan._PreRegisterAllGenericTemplates(mb, result)
# ============================================================
# Phase 1a: 注册 struct/enum/union(mark1 前,全量 includes)
@@ -552,8 +496,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
if topo_count > 0:
od_addr: uint64 = uint64(topo_order) + i * 4
od_ptr: t.CPtr = (int*)(t.CPtr(od_addr))
if od_ptr is not None:
file_idx = od_ptr[0]
if od_ptr is not None: file_idx = od_ptr[0]
entry_addr_r: uint64 = uint64(result.Entries) + file_idx * entry_size
entry_r: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr_r)
@@ -606,8 +549,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
stdlib.free(src_buf_r)
p1a_fl += 1
continue
if bytes_read_r < Phase1.SRC_BUF_SIZE:
src_buf_r[bytes_read_r] = 0
if bytes_read_r < Phase1.SRC_BUF_SIZE: src_buf_r[bytes_read_r] = 0
# 解析 AST
lx_r: ast.Lexer* = ast.new_lexer(mb)
@@ -644,19 +586,15 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
ret_r: int = tr_r.translate(tree_r)
# 计时点 2:translate(declare=1) 返回
t_fa2: uint64 = w32.win32base.GetTickCount64()
if ret_r != 0:
p1a_fl += 1
else:
p1a_reg += 1
if ret_r != 0: p1a_fl += 1
else: p1a_reg += 1
# 逐文件进度日志(parse/translate 两段,定位 1a 卡点)
VLogger.info(f"[{i+1}/{iter_count}] 1a: {file_path_r} (parse={t_fa1-t_fa0} ms, translate={t_fa2-t_fa1} ms, total={t_fa2-t_fa0} ms)", "Phase1")
# 释放 Translator 的 C malloc 资源
if tr_r._global_names is not None:
stdlib.free(tr_r._global_names)
if tr_r._nonlocal_names is not None:
stdlib.free(tr_r._nonlocal_names)
if tr_r._global_names is not None: stdlib.free(tr_r._global_names)
if tr_r._nonlocal_names is not None: stdlib.free(tr_r._nonlocal_names)
stdlib.free(src_buf_r)
# [TEMP-DIAG] 1a 每文件哨兵:校验本文件窗口 [p1a_mark, chk_depth) 内所有存活块的块尾 canary。
@@ -683,16 +621,11 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
# 释放拓扑排序资源(topo_order/topo_count_box 保留到 Phase 1b 结束,
# 1b 按拓扑序翻译需要它们;class/def 数组 1b 不再使用)
if class_names_buf is not None:
mb.free(class_names_buf)
if parent_names_buf is not None:
mb.free(parent_names_buf)
if def_sha1s_buf is not None:
mb.free(def_sha1s_buf)
if generic_file_indices is not None:
mb.free(generic_file_indices)
if generic_file_count_box is not None:
mb.free(generic_file_count_box)
if class_names_buf is not None: mb.free(class_names_buf)
if parent_names_buf is not None: mb.free(parent_names_buf)
if def_sha1s_buf is not None: mb.free(def_sha1s_buf)
if generic_file_indices is not None: mb.free(generic_file_indices)
if generic_file_count_box is not None: mb.free(generic_file_count_box)
# ============================================================
# Phase 1b: 全量翻译(struct 已注册,走 existing 路径翻译方法体)
@@ -722,12 +655,9 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
continue
sha1_pv: str = entry_pv.Sha1
in_set_pv: int = 0
if use_reachable != 0:
in_set_pv = StubMergerCollect._is_in_sha1_set(sha1_pv, reachable_set, reachable_count)
else:
in_set_pv = StubMergerCollect._is_in_sha1_set(sha1_pv, sha1_set, set_count)
if in_set_pv == 0:
continue
if use_reachable != 0: in_set_pv = StubMergerCollect._is_in_sha1_set(sha1_pv, reachable_set, reachable_count)
else: in_set_pv = StubMergerCollect._is_in_sha1_set(sha1_pv, sha1_set, set_count)
if in_set_pv == 0: continue
# vpi 已存在则跳过
vpi_path_pv: str = StubMergerCollect._sliced_path(temp_dir, dir_len_pv, sha1_pv, "vpi")
if vpi_path_pv is None:
@@ -766,8 +696,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 文件读取失败或为空", "Phase1")
continue
if bytes_read_pv < Phase1.SRC_BUF_SIZE:
src_buf_pv[bytes_read_pv] = 0
if bytes_read_pv < Phase1.SRC_BUF_SIZE: src_buf_pv[bytes_read_pv] = 0
lx_pv: ast.Lexer* = ast.new_lexer(mb)
if lx_pv is None:
stdlib.free(src_buf_pv)
@@ -832,16 +761,14 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
t_b0: uint64 = w32.win32base.GetTickCount64()
loop_n_b: int = result.Count
if topo_count > 0:
loop_n_b = topo_count
if topo_count > 0: loop_n_b = topo_count
for i in range(loop_n_b):
# 获取 entry(拓扑序:file_idx 从 topo_order 取,否则用扫描索引)
file_idx_b: int = i
if topo_count > 0:
od_addr_b: uint64 = uint64(topo_order) + sizet(i) * 4
od_ptr_b: t.CPtr = (int*)(od_addr_b)
if od_ptr_b is not None:
file_idx_b = od_ptr_b[0]
if od_ptr_b is not None: file_idx_b = od_ptr_b[0]
entry_addr: uint64 = uint64(result.Entries) + sizet(file_idx_b) * entry_size
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
if entry is None:
@@ -857,10 +784,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
in_set: int = 1
if topo_count == 0:
in_set = 0
if use_reachable != 0:
in_set = StubMergerCollect._is_in_sha1_set(sha1, reachable_set, reachable_count)
else:
in_set = StubMergerCollect._is_in_sha1_set(sha1, sha1_set, set_count)
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1, reachable_set, reachable_count)
else: in_set = StubMergerCollect._is_in_sha1_set(sha1, sha1_set, set_count)
if in_set == 0:
skipped += 1
continue
@@ -886,26 +811,21 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
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
if vrd_1b < 1048576: vbuf_1b[vrd_1b] = 0
vtotal_1b: sizet = string.strlen(vbuf_1b)
vpos_1b: sizet = 0
while vpos_1b < vtotal_1b:
vls_1b: sizet = vpos_1b
while vpos_1b < vtotal_1b and vbuf_1b[vpos_1b] != '\n':
vpos_1b += 1
while vpos_1b < vtotal_1b and vbuf_1b[vpos_1b] != '\n': vpos_1b += 1
vll_1b: sizet = vpos_1b - vls_1b
if vpos_1b < vtotal_1b:
vpos_1b += 1
if vpos_1b < vtotal_1b: vpos_1b += 1
vi2_1b: sizet = vls_1b
while vi2_1b < vls_1b + vll_1b and vbuf_1b[vi2_1b] == ' ':
vi2_1b += 1
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()
else: vf_1b.close()
stdlib.free(vpi_path_1b)
if force_translate == 1:
# 含 class:强制翻译(覆盖 stub/text,重新注册类)
@@ -990,8 +910,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
stdlib.free(src_buf)
failed += 1
continue
if bytes_read < Phase1.SRC_BUF_SIZE:
src_buf[bytes_read] = 0
if bytes_read < Phase1.SRC_BUF_SIZE: src_buf[bytes_read] = 0
# 解析 AST
lx: ast.Lexer* = ast.new_lexer(mb)
@@ -1073,15 +992,12 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
t_f2: uint64 = w32.win32base.GetTickCount64()
# 诊断:translate 正常返回(若挂起在 translate 内部,此行不会打印,
# 用于区分"translate 内部挂起"与"后续 dump 挂起")
if ret == 0:
VLogger.info(f"翻译完成(translate返回): {file_path}", "Phase1")
if ret == 0: VLogger.info(f"翻译完成(translate返回): {file_path}", "Phase1")
if ret != 0:
VLogger.error(f"翻译失败: {file_path}", "Phase1") # error 终止
stdlib.free(src_buf)
if tr._global_names is not None:
stdlib.free(tr._global_names)
if tr._nonlocal_names is not None:
stdlib.free(tr._nonlocal_names)
if tr._global_names is not None: stdlib.free(tr._global_names)
if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names)
failed += 1
continue
@@ -1089,14 +1005,12 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
# (its stub/text.ll below are what Phase2 reuses to build the .obj). The
# injector lives in the Gen layer (already depends on mlirlite), so this
# module gains no cross-module mlirlite symbol dependency.
if BuildPipeline._traceback_mode != 0 and BuildPipeline._tb_basename_is_traceback(file_path) != 0:
HandlesTranslator.InjectTracebackInstallCtor(tr)
if BuildPipeline._traceback_mode != 0 and BuildPipeline._tb_basename_is_traceback(file_path) != 0: HandlesTranslator.InjectTracebackInstallCtor(tr)
# 诊断: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")
if post_rc != 0: VLogger.error(f"内存损坏(translate后存活块, rc={post_rc}): {file_path}", "Phase1")
# dump stub IR(declarations only)
# 1MB 不够:ast/parser.vp 的 text.ll 超 1MB 被截断 → 合并 IR 尾部丢字
@@ -1107,10 +1021,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
stub_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE)
if stub_buf is None:
stdlib.free(src_buf)
if tr._global_names is not None:
stdlib.free(tr._global_names)
if tr._nonlocal_names is not None:
stdlib.free(tr._nonlocal_names)
if tr._global_names is not None: stdlib.free(tr._global_names)
if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names)
failed += 1
continue
tr.dump_ir(stub_buf, PHASE1_IR_SIZE, mlirlite.OUTPUT_STUB)
@@ -1131,10 +1043,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
text_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE)
if text_buf is None:
stdlib.free(src_buf)
if tr._global_names is not None:
stdlib.free(tr._global_names)
if tr._nonlocal_names is not None:
stdlib.free(tr._nonlocal_names)
if tr._global_names is not None: stdlib.free(tr._global_names)
if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names)
failed += 1
continue
tr.dump_ir(text_buf, PHASE1_IR_SIZE, mlirlite.OUTPUT_TEXT)
@@ -1156,8 +1066,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
# save {sha1}.full.ll(在线 FULL 产物:模型在线时的自足单模块 IR,供 Phase B 直接编译,
# 取代 stub + 依赖 stub/text 文本合并)
full_buf_p1: bytes = stdlib.malloc(PHASE1_FULL_SIZE)
if full_buf_p1 is None:
VLogger.error("full.ll 缓冲区分配失败", "Phase1")
if full_buf_p1 is None: VLogger.error("full.ll 缓冲区分配失败", "Phase1")
tr.dump_ir(full_buf_p1, PHASE1_FULL_SIZE, mlirlite.OUTPUT_FULL)
full_len_p1: sizet = string.strlen(full_buf_p1)
full_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "full.ll")
@@ -1193,10 +1102,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
stdlib.free(deps_path_p1)
# 释放 Translator 的 C malloc 资源
if tr._global_names is not None:
stdlib.free(tr._global_names)
if tr._nonlocal_names is not None:
stdlib.free(tr._nonlocal_names)
if tr._global_names is not None: stdlib.free(tr._global_names)
if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names)
# 所有 dump_ir 完成,src_buf 不再需要
stdlib.free(src_buf)
@@ -1210,23 +1117,19 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
# 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")
if chk_rc != 0: VLogger.error(f"内存损坏(存活块 header, rc={chk_rc}): {file_path}", "Phase1")
# 越界写是崩溃根因(与回收/UAF 无关,已由 A/B 实验确认)——恢复 reset_to,改用块尾 canary(check_alive_blocks 校验)把越界写事发地定位到具体文件。
mb.reset_to(mark1)
sc_rc: int = mb.self_check()
if sc_rc != 0:
VLogger.error(f"内存损坏(free list, rc={sc_rc}): {file_path}", "Phase1")
if sc_rc != 0: VLogger.error(f"内存损坏(free list, rc={sc_rc}): {file_path}", "Phase1")
# 1b 阶段总耗时(全量翻译 + dump)
t_b1: uint64 = w32.win32base.GetTickCount64()
VLogger.info(f"1b 总耗时: {t_b1-t_b0} ms (全量翻译, trans={translated} skip={skipped} fail={failed})", "Phase1")
# 释放 Phase 1b 使用的拓扑排序资源
if topo_order is not None:
mb.free(topo_order)
if topo_count_box is not None:
mb.free(topo_count_box)
if topo_order is not None: mb.free(topo_order)
if topo_count_box is not None: mb.free(topo_count_box)
VLogger.info(f"完成: 翻译={translated} 跳过={skipped} 失败={failed}", "Phase1")
@@ -1235,7 +1138,6 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
return 1
# 释放可达 SHA1 集合(如果分配了)
if reachable_set is not None:
stdlib.free(reachable_set)
if reachable_set is not None: stdlib.free(reachable_set)
return 0
+57 -114
View File
@@ -64,12 +64,10 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
generic_file_indices: t.CPtr,
generic_file_count_box: t.CPtr) -> int:
"""预扫描 includes 文件,收集类继承关系"""
if result is None:
return -1
if result is None: return -1
# class_names_buf/parent_names_buf/def_sha1s_buf 由调用方 mb.alloc 分配(Phase1Main 预扫描前),本函数把其地址直接作为 strcpy 目的缓冲;
# 分配失败返回 NULL 时目的指针为近零地址 → strcpy 写零页(ASan access-violation),故按 fail-fast 显式报错终止(VLogger.error 内部 sys.exit(1)),不静默返回 -1。
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败(class_names_buf/parent_names_buf/def_sha1s_buf 为 NULL)", "Phase1")
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败(class_names_buf/parent_names_buf/def_sha1s_buf 为 NULL)", "Phase1")
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
class_count: int = 0
@@ -80,26 +78,21 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
scan_mark: sizet = mb.mark()
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
if entry is None or entry.Sha1 is None:
continue
if entry is None or entry.Sha1 is None: continue
sha1_e: str = entry.Sha1
file_has_generic: int = 0
# 按需翻译过滤
in_set: int = 0
if use_reachable != 0:
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else:
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0:
continue
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0: continue
# 读取文件
file_path: str = entry.Path
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
if f.closed:
continue
if f.closed: continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f.size() >= Phase1.SRC_BUF_SIZE:
@@ -117,8 +110,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
if bytes_read <= 0:
stdlib.free(src_buf)
continue
if bytes_read < Phase1.SRC_BUF_SIZE:
src_buf[bytes_read] = 0
if bytes_read < Phase1.SRC_BUF_SIZE: src_buf[bytes_read] = 0
# 解析 AST
lx: ast.Lexer* = ast.new_lexer(mb)
@@ -138,15 +130,12 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
cn: sizet = len(ch)
for ci in range(cn):
child: ast.AST* = ch.get(ci)
if child is None:
continue
if child is None: continue
kd: int = child.kind()
if kd != ast.ASTKind.ClassDef:
continue
if kd != ast.ASTKind.ClassDef: continue
cd: ast.ClassDef* = ast.ClassDef*(child)
if cd is None or cd.name is None:
continue
if cd is None or cd.name is None: continue
# 检测泛型类(有 type_params),记录文件索引以供 Phase 1a 预注册
if file_has_generic == 0:
@@ -160,12 +149,10 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
if gfc_scan < Phase1.MAX_FILE_DEPS and generic_file_indices is not None:
gi_addr_scan: uint64 = uint64(generic_file_indices) + gfc_scan * 4
gi_ptr_scan: t.CPtr = (int*)(gi_addr_scan)
if gi_ptr_scan is not None:
gi_ptr_scan[0] = i
if gi_ptr_scan is not None: gi_ptr_scan[0] = i
gfc_ptr_scan[0] = gfc_scan + 1
if class_count >= Phase1.MAX_CLASSES:
break
if class_count >= Phase1.MAX_CLASSES: break
# 填充扁平数组:类名(偏移 = class_count * Phase1.CLASS_NAME_LEN)
name_dst: str = class_names_buf + class_count * Phase1.CLASS_NAME_LEN
@@ -173,8 +160,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
if nm_len >= Phase1.CLASS_NAME_LEN:
string.strncpy(name_dst, cd.name, Phase1.CLASS_NAME_LEN - 1)
name_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
else:
string.strcpy(name_dst, cd.name)
else: string.strcpy(name_dst, cd.name)
# 填充扁平数组:父类名(无父类填 '\0')
parent_dst: str = parent_names_buf + class_count * Phase1.CLASS_NAME_LEN
@@ -186,8 +172,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
if pnm_len >= Phase1.CLASS_NAME_LEN:
string.strncpy(parent_dst, parent_nm, Phase1.CLASS_NAME_LEN - 1)
parent_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
else:
string.strcpy(parent_dst, parent_nm)
else: string.strcpy(parent_dst, parent_nm)
# 填充扁平数组:定义 SHA1(偏移 = class_count * Phase1.SHA1_LEN)
sha1_dst: str = def_sha1s_buf + class_count * Phase1.SHA1_LEN
@@ -209,8 +194,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
# ============================================================
def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
"""文本扫描:buf 中是否有 'class 标识符[' 泛型类标记,返回 1/0"""
if buf is None or blen <= 6:
return 0
if buf is None or blen <= 6: return 0
pos: sizet = 0
while pos + 5 < blen:
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' and buf[pos + 3] == 's' and buf[pos + 4] == 's':
@@ -218,13 +202,11 @@ def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
prev_ok: int = 1
if pos > 0:
pc: char = buf[pos - 1]
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_':
prev_ok = 0
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
if prev_ok != 0:
p: sizet = pos + 5
# 跳过空白
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
p += 1
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
# 读类名标识符
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z')
or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
@@ -234,13 +216,10 @@ def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
or buf[p] == '_'):
p += 1
# 类名后非空白是 '[' → 泛型类
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
p += 1
if p < blen and buf[p] == '[':
return 1
pos += 1
else:
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
if p < blen and buf[p] == '[': return 1
pos += 1
else: pos += 1
return 0
# ============================================================
# _BufHasGenericFuncMarker - 文本扫描检测文件是否含泛型函数标记
@@ -252,8 +231,7 @@ def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
# ============================================================
def _BufHasGenericFuncMarker(buf: bytes, blen: sizet) -> int:
"""文本扫描:buf 中是否有 'def 标识符[' 泛型函数标记,返回 1/0"""
if buf is None or blen <= 4:
return 0
if buf is None or blen <= 4: return 0
pos: sizet = 0
while pos + 3 < blen:
if buf[pos] == 'd' and buf[pos + 1] == 'e' and buf[pos + 2] == 'f':
@@ -261,13 +239,11 @@ def _BufHasGenericFuncMarker(buf: bytes, blen: sizet) -> int:
prev_ok: int = 1
if pos > 0:
pc: char = buf[pos - 1]
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_':
prev_ok = 0
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
if prev_ok != 0:
p: sizet = pos + 3
# 跳过空白
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
p += 1
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
# 读函数名标识符
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z')
or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
@@ -277,13 +253,10 @@ def _BufHasGenericFuncMarker(buf: bytes, blen: sizet) -> int:
or buf[p] == '_'):
p += 1
# 函数名后非空白是 '[' → 泛型函数
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
p += 1
if p < blen and buf[p] == '[':
return 1
pos += 1
else:
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
if p < blen and buf[p] == '[': return 1
pos += 1
else: pos += 1
return 0
# ============================================================
# _PreRegisterAllGenericTemplates - 预注册 includes 泛型类模板(mark1 前)
@@ -296,24 +269,20 @@ def _BufHasGenericFuncMarker(buf: bytes, blen: sizet) -> int:
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
result: rawptr[IncludesScanner.ScanResult]) -> int:
"""预注册全部 includes 文件的泛型类模板,返回处理到的泛型类数量"""
if result is None or mb is None:
return 0
if result is None or mb is None: return 0
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if perm is None:
return 0
if perm is None: return 0
p1e_size: sizet = IncludesScanner.FileEntry.__sizeof__()
handled_count: int = 0
for i in range(result.Count):
entry_addr_pg: uint64 = uint64(result.Entries) + i * p1e_size
entry_pg: rawptr[IncludesScanner.FileEntry] = (
rawptr[IncludesScanner.FileEntry])(entry_addr_pg)
if entry_pg is None or entry_pg.Sha1 is None:
continue
if entry_pg is None or entry_pg.Sha1 is None: continue
# 读取文件
file_path_pg: str = entry_pg.Path
f_pg: rawptr[fileio.File] = fileio.File(file_path_pg, fileio.MODE.R)
if f_pg.closed:
continue
if f_pg.closed: continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_pg}", "Phase1")
@@ -328,8 +297,7 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
if bytes_read_pg <= 0:
stdlib.free(src_buf_pg)
continue
if bytes_read_pg < Phase1.SRC_BUF_SIZE:
src_buf_pg[bytes_read_pg] = 0
if bytes_read_pg < Phase1.SRC_BUF_SIZE: src_buf_pg[bytes_read_pg] = 0
# 文本扫描筛选:非泛型文件跳过(不 parse、不驻留,防 OOM)。
# 泛型类(class X[)与泛型函数(def f[)任一命中即进入 parse。
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0 and _BufHasGenericFuncMarker(src_buf_pg, bytes_read_pg) == 0:
@@ -352,34 +320,27 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
cn_pg: sizet = len(ch_pg)
for ci_pg in range(cn_pg):
child_pg: ast.AST* = ch_pg.get(ci_pg)
if child_pg is None:
continue
if child_pg is None: continue
ck_pg: int = child_pg.kind()
if ck_pg == ast.ASTKind.ClassDef:
cd_pg: ast.ClassDef* = ast.ClassDef*(child_pg)
if cd_pg is None or cd_pg.name is None:
continue
if cd_pg is None or cd_pg.name is None: continue
tp_pg: list[str]* = cd_pg.type_params
if tp_pg is None:
continue
if tp_pg is None: continue
GenApi.set_current_file_type(file_path_pg)
GenApi._register_generic_template(perm, cd_pg, entry_pg.Sha1)
handled_count += 1
elif ck_pg == ast.ASTKind.FunctionDef:
fd_pg: ast.FunctionDef* = ast.FunctionDef*(child_pg)
if fd_pg is None or fd_pg.name is None:
continue
if fd_pg is None or fd_pg.name is None: continue
tf_pg: list[str]* = fd_pg.type_params
if tf_pg is None:
continue
if tf_pg is None: continue
GenApi.set_current_file_type(file_path_pg)
GenApi._register_generic_func_template(perm, fd_pg, entry_pg.Sha1)
handled_count += 1
stdlib.free(src_buf_pg)
if handled_count > 0:
VLogger.info(f"mark1 前永久池预注册 {handled_count} 个 includes 泛型模板(类+函数)", "Phase1")
else:
VLogger.info(f"mark1 前永久池预注册 0 个泛型模板(扫描 {result.Count} 个文件,可能 marker 未命中或全量扫描失败)", "Phase1")
if handled_count > 0: VLogger.info(f"mark1 前永久池预注册 {handled_count} 个 includes 泛型模板(类+函数)", "Phase1")
else: VLogger.info(f"mark1 前永久池预注册 0 个泛型模板(扫描 {result.Count} 个文件,可能 marker 未命中或全量扫描失败)", "Phase1")
return handled_count
# ============================================================
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
@@ -390,12 +351,10 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes,
class_count: int, class_name: str) -> str:
"""查找类名对应的定义文件 SHA1"""
if class_names_buf is None or def_sha1s_buf is None or class_name is None:
return None
if class_names_buf is None or def_sha1s_buf is None or class_name is None: return None
for i in range(class_count):
name_ptr: str = class_names_buf + i * Phase1.CLASS_NAME_LEN
if string.strcmp(name_ptr, class_name) == 0:
return def_sha1s_buf + i * Phase1.SHA1_LEN
if string.strcmp(name_ptr, class_name) == 0: return def_sha1s_buf + i * Phase1.SHA1_LEN
return None
# ============================================================
# _ScanTreeForContainers - 递归扫描 AST 检测 list/dict 容器使用
@@ -416,16 +375,13 @@ def _AnnIsContainer(ann: ast.AST*) -> int:
if ak_c == ast.ASTKind.Name:
nm_c: ast.Name* = ast.Name*(ann)
if nm_c.id is not None:
if string.strcmp(nm_c.id, "list") == 0:
return 1
if string.strcmp(nm_c.id, "dict") == 0:
return 1
if string.strcmp(nm_c.id, "list") == 0: return 1
if string.strcmp(nm_c.id, "dict") == 0: return 1
return 0
elif ak_c == ast.ASTKind.Subscript:
sub_c: ast.Subscript* = ast.Subscript*(ann)
ann = sub_c.value
else:
return 0
else: return 0
return 0
@@ -436,18 +392,14 @@ def _ScanTreeForContainers(node: ast.AST*,
"""递归扫描 AST(字段 + 语句块),检测 list/dict 容器使用"""
# 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限
# 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。
if depth > 128:
VLogger.error("容器扫描递归过深(AST 环或嵌套超过 128 层)", "Phase1")
if node is None:
return
if depth > 128: VLogger.error("容器扫描递归过深(AST 环或嵌套超过 128 层)", "Phase1")
if node is None: return
k_s: int = node.kind()
if k_s == ast.ASTKind.Name:
nm_s: ast.Name* = ast.Name*(node)
if nm_s.id is not None:
if string.strcmp(nm_s.id, "list") == 0:
need_list_box[0] = 1
elif string.strcmp(nm_s.id, "dict") == 0:
need_dict_box[0] = 1
if string.strcmp(nm_s.id, "list") == 0: need_list_box[0] = 1
elif string.strcmp(nm_s.id, "dict") == 0: need_dict_box[0] = 1
elif k_s == ast.ASTKind.Subscript:
sub_s: ast.Subscript* = ast.Subscript*(node)
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1)
@@ -463,16 +415,14 @@ def _ScanTreeForContainers(node: ast.AST*,
if cl_args_s is not None:
na_s: sizet = len(cl_args_s)
i_a2: sizet
for i_a2 in range(na_s):
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
for i_a2 in range(na_s): _ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
if cl_kws_s is not None:
nk_s: sizet = len(cl_kws_s)
i_k2: sizet
for i_k2 in range(nk_s):
kw_s: rawptr[ast.Keyword] = rawptr[ast.Keyword](cl_kws_s.get(i_k2))
if kw_s is not None:
_ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
if kw_s is not None: _ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Assign:
asn_s: ast.Assign* = ast.Assign*(node)
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
@@ -480,8 +430,7 @@ def _ScanTreeForContainers(node: ast.AST*,
if tgts_s is not None:
nt_s: sizet = len(tgts_s)
i_t2: sizet
for i_t2 in range(nt_s):
_ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
for i_t2 in range(nt_s): _ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.AnnAssign:
aa_s2: ast.AnnAssign* = ast.AnnAssign*(node)
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1)
@@ -497,16 +446,14 @@ def _ScanTreeForContainers(node: ast.AST*,
i_f2: sizet
for i_f2 in range(nf_s):
arg_s: ast.Arg* = ast.Arg*(fa_s.get(i_f2))
if arg_s is not None:
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
if arg_s is not None: _ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.ClassDef:
cd_s: ast.ClassDef* = ast.ClassDef*(node)
bases_s: list[ast.AST*]* = cd_s.bases
if bases_s is not None:
nb_s: sizet = len(bases_s)
i_b2: sizet
for i_b2 in range(nb_s):
_ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
for i_b2 in range(nb_s): _ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Attribute:
at_s: ast.Attribute* = ast.Attribute*(node)
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
@@ -516,8 +463,7 @@ def _ScanTreeForContainers(node: ast.AST*,
if elts_s is not None:
ne_s: sizet = len(elts_s)
i_e2: sizet
for i_e2 in range(ne_s):
_ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
for i_e2 in range(ne_s): _ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.UnaryOp:
uop_s: ast.UnaryOp* = ast.UnaryOp*(node)
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1)
@@ -534,16 +480,14 @@ def _ScanTreeForContainers(node: ast.AST*,
if cps_s is not None:
nc_s: sizet = len(cps_s)
i_c2: sizet
for i_c2 in range(nc_s):
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
for i_c2 in range(nc_s): _ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.BoolOp:
bo_s: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
bvs_s: list[ast.AST*]* = bo_s.values
if bvs_s is not None:
nv_s: sizet = len(bvs_s)
i_v2: sizet
for i_v2 in range(nv_s):
_ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
for i_v2 in range(nv_s): _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
# `[...]`/`{}` 字面量不触发 _list/_dict 导入:字面量广泛用于编译期常量列表(c.Asm 的 op/inp/out CLOBBER 参数)与数组数据初始化,并非容器使用;
# 容器的真实使用由类型位置检测覆盖(Name/Subscript 注解、Call.func 构造名)。
# elif k_s == ast.ASTKind.List:
@@ -555,5 +499,4 @@ def _ScanTreeForContainers(node: ast.AST*,
ch_s: list[ast.AST*]* = node.children
n_s: sizet = len(ch_s)
ci_s: sizet
for ci_s in range(n_s):
_ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
for ci_s in range(n_s): _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
+40 -80
View File
@@ -55,8 +55,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
out_order: t.CPtr, out_count: t.CPtr,
temp_dir: str, td_len_ts: sizet) -> int:
"""根据类继承关系对文件进行拓扑排序"""
if result is None or out_order is None or out_count is None:
return -1
if result is None or out_order is None or out_count is None: return -1
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
@@ -69,16 +68,11 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
file_processed: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None:
if file_sha1s_buf is not None:
mb.free(file_sha1s_buf)
if file_deps_buf is not None:
mb.free(file_deps_buf)
if file_dep_counts is not None:
mb.free(file_dep_counts)
if file_indegrees is not None:
mb.free(file_indegrees)
if file_processed is not None:
mb.free(file_processed)
if file_sha1s_buf is not None: mb.free(file_sha1s_buf)
if file_deps_buf is not None: mb.free(file_deps_buf)
if file_dep_counts is not None: mb.free(file_dep_counts)
if file_indegrees is not None: mb.free(file_indegrees)
if file_processed is not None: mb.free(file_processed)
return -1
string.memset(file_sha1s_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
@@ -92,20 +86,15 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
for i in range(result.Count):
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
if entry is None or entry.Sha1 is None:
continue
if entry is None or entry.Sha1 is None: continue
sha1_e: str = entry.Sha1
in_set: int = 0
if use_reachable != 0:
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else:
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0:
continue
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0: continue
if file_count >= Phase1.MAX_FILE_DEPS:
break
if file_count >= Phase1.MAX_FILE_DEPS: break
# 初始化扁平数组:文件 SHA1(偏移 = file_count * Phase1.SHA1_LEN)
# DepCount/InDegree/Processed 已由 memset 清零
@@ -118,8 +107,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
for ci in range(class_count):
# 读取父类名(偏移 = ci * Phase1.CLASS_NAME_LEN)
parent_ptr: str = parent_names_buf + ci * Phase1.CLASS_NAME_LEN
if parent_ptr[0] == '\0':
continue
if parent_ptr[0] == '\0': continue
# 读取子类定义 SHA1(偏移 = ci * Phase1.SHA1_LEN)
child_def_sha1: str = def_sha1s_buf + ci * Phase1.SHA1_LEN
@@ -130,20 +118,17 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
continue # 父类不在 includes 中(如 Object、CEnum 等标记基类)
# 父类文件 SHA1 不能等于子类文件 SHA1(同文件内继承不需要依赖)
if string.strcmp(parent_sha1, child_def_sha1) == 0:
continue
if string.strcmp(parent_sha1, child_def_sha1) == 0: continue
# 找到子类文件对应的索引,添加父文件 SHA1 到依赖列表
for fi in range(file_count):
fsha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
if string.strcmp(fsha1, child_def_sha1) != 0:
continue
if string.strcmp(fsha1, child_def_sha1) != 0: continue
# 读取当前依赖数量
cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4
cnt_ptr: t.CPtr = (int*)(cnt_addr)
if cnt_ptr is None:
break
if cnt_ptr is None: break
cur_cnt: int = cnt_ptr[0]
# 检查依赖是否已添加(去重)
@@ -153,8 +138,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
if string.strcmp(dep_slot, parent_sha1) == 0:
already = 1
break
if already != 0:
break
if already != 0: break
if cur_cnt < Phase1.MAX_DEP_EDGES:
dep_dst: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + cur_cnt * Phase1.SHA1_LEN
@@ -174,44 +158,34 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
map_count_ts = StubMergerIR._BuildIncludesSha1Map(
temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts)
for fi_imp in range(file_count):
if map_count_ts <= 0:
break
if map_count_ts <= 0: break
fsha1_imp: str = file_sha1s_buf + fi_imp * Phase1.SHA1_LEN
deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp)
if deps_imp is None:
continue
if deps_imp is None: continue
dl_imp: sizet = string.strlen(deps_imp)
dpos_imp: sizet = 0
while dpos_imp < dl_imp:
dstart_imp: sizet = dpos_imp
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ':
dpos_imp += 1
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ': dpos_imp += 1
dlen_imp: sizet = dpos_imp - dstart_imp
if dpos_imp < dl_imp:
dpos_imp += 1
if dlen_imp == 0:
continue
if dpos_imp < dl_imp: dpos_imp += 1
if dlen_imp == 0: continue
dname_imp: bytes = stdlib.malloc(dlen_imp + 1)
if dname_imp is None:
continue
if dname_imp is None: continue
string.strncpy(dname_imp, deps_imp + dstart_imp, dlen_imp)
dname_imp[dlen_imp] = '\0'
dsha1_imp: str = StubMergerIR._FindSha1ByModName(
sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp)
stdlib.free(dname_imp)
if dsha1_imp is None:
continue
if dsha1_imp is None: continue
# 找依赖文件在列表中的索引(跳过自身)
for fj_imp in range(file_count):
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0:
continue
if fj_imp == fi_imp:
break
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0: continue
if fj_imp == fi_imp: break
# 去重 + 加边:fi_imp 依赖 fj_imp(InDegree[fi_imp]++)
cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4
cnt_ptr_imp: t.CPtr = (int*)(cnt_addr_imp)
if cnt_ptr_imp is None:
break
if cnt_ptr_imp is None: break
cur_cnt_imp: int = cnt_ptr_imp[0]
already_imp: int = 0
for di_imp in range(cur_cnt_imp):
@@ -226,10 +200,8 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
break
# 释放 import 边用的局部映射数组
if sha1_arr_ts is not None:
stdlib.free(sha1_arr_ts)
if mod_arr_ts is not None:
stdlib.free(mod_arr_ts)
if sha1_arr_ts is not None: stdlib.free(sha1_arr_ts)
if mod_arr_ts is not None: stdlib.free(mod_arr_ts)
# 3. Kahn 算法:InDegree = DepCount(依赖多少个父文件)
# 依赖为 0 的先处理,处理后减少依赖它的文件入度
@@ -238,8 +210,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
cnt_ptr3: t.CPtr = (int*)(cnt_addr3)
ind_addr3: uint64 = uint64(file_indegrees) + fi * 4
ind_ptr3: t.CPtr = (int*)(ind_addr3)
if cnt_ptr3 is not None and ind_ptr3 is not None:
ind_ptr3[0] = cnt_ptr3[0]
if cnt_ptr3 is not None and ind_ptr3 is not None: ind_ptr3[0] = cnt_ptr3[0]
# 4. 拓扑排序输出
out_idx: int = 0
@@ -250,13 +221,11 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
# 检查处理状态
proc_addr4: uint64 = uint64(file_processed) + fi * 4
proc_ptr4: t.CPtr = (int*)(proc_addr4)
if proc_ptr4 is None or proc_ptr4[0] != 0:
continue
if proc_ptr4 is None or proc_ptr4[0] != 0: continue
# 检查入度
ind_addr4: uint64 = uint64(file_indegrees) + fi * 4
ind_ptr4: t.CPtr = (int*)(ind_addr4)
if ind_ptr4 is None or ind_ptr4[0] > 0:
continue
if ind_ptr4 is None or ind_ptr4[0] > 0: continue
# 入度为 0,可以处理
# 找到对应的 result.Entries 索引
@@ -269,8 +238,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
# 写入输出索引数组
out_idx_addr: uint64 = uint64(out_order) + out_idx * 4
out_int_ptr: t.CPtr = (int*)(out_idx_addr)
if out_int_ptr is not None:
out_int_ptr[0] = ri
if out_int_ptr is not None: out_int_ptr[0] = ri
out_idx += 1
break
@@ -282,21 +250,18 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
for fj in range(file_count):
proc_addr5: uint64 = uint64(file_processed) + fj * 4
proc_ptr5: t.CPtr = (int*)(proc_addr5)
if proc_ptr5 is None or proc_ptr5[0] != 0:
continue
if proc_ptr5 is None or proc_ptr5[0] != 0: continue
# 读取 fj 的依赖数量
cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4
cnt_ptr5: t.CPtr = (int*)(cnt_addr5)
if cnt_ptr5 is None:
continue
if cnt_ptr5 is None: continue
fj_dep_cnt: int = cnt_ptr5[0]
for di in range(fj_dep_cnt):
dep_slot5: str = file_deps_buf + fj * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
if string.strcmp(dep_slot5, target_sha1) == 0:
ind_addr5: uint64 = uint64(file_indegrees) + fj * 4
ind_ptr5: t.CPtr = (int*)(ind_addr5)
if ind_ptr5 is not None:
ind_ptr5[0] = ind_ptr5[0] - 1
if ind_ptr5 is not None: ind_ptr5[0] = ind_ptr5[0] - 1
break
# 处理循环依赖:环内优先输出被依赖者(被其余剩余文件依赖数量多者先)。
@@ -321,12 +286,10 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
target_sha1_a: str = file_sha1s_buf + fi_a * Phase1.SHA1_LEN
for i_b in range(rem_count):
fi_b: int = rem_idx[i_b]
if fi_b == fi_a:
continue
if fi_b == fi_a: continue
cnt_addr_b: uint64 = uint64(file_dep_counts) + fi_b * 4
cnt_ptr_b: t.CPtr = (int*)(cnt_addr_b)
if cnt_ptr_b is None:
continue
if cnt_ptr_b is None: continue
cnt_b: int = cnt_ptr_b[0]
for di_b in range(cnt_b):
dep_slot_b: str = file_deps_buf + fi_b * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_b * Phase1.SHA1_LEN
@@ -348,8 +311,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
fi_a2: int = rem_idx[i_a]
proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4
proc_ptr8: t.CPtr = (int*)(proc_addr8)
if proc_ptr8 is None or proc_ptr8[0] != 0:
continue
if proc_ptr8 is None or proc_ptr8[0] != 0: continue
target_sha1_2: str = file_sha1s_buf + fi_a2 * Phase1.SHA1_LEN
for ri in range(result.Count):
ea_r2: uint64 = uint64(result.Entries) + ri * entry_size
@@ -358,8 +320,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0:
out_idx_addr2: uint64 = uint64(out_order) + out_idx * 4
out_int_ptr2: t.CPtr = (int*)(out_idx_addr2)
if out_int_ptr2 is not None:
out_int_ptr2[0] = ri
if out_int_ptr2 is not None: out_int_ptr2[0] = ri
out_idx += 1
break
proc_ptr8[0] = 1
@@ -368,8 +329,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
# 写入输出数量
out_count_ptr: t.CPtr = (int*)(out_count)
if out_count_ptr is not None:
out_count_ptr[0] = out_idx
if out_count_ptr is not None: out_count_ptr[0] = out_idx
# 释放扁平数组
mb.free(file_sha1s_buf)
+137 -274
View File
@@ -56,15 +56,12 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy*,
entry_size: sizet,
file_count: int, max_files: int) -> int:
"""递归扫描目录,收集 .vp/.py 文件到 entries,返回新的 file_count"""
if dir_path is None or entries is None:
return file_count
if file_count >= max_files:
return file_count
if dir_path is None or entries is None: return file_count
if file_count >= max_files: return file_count
dir_len: sizet = string.strlen(dir_path)
pattern: bytes = stdlib.malloc(dir_len + 16)
if pattern is None:
return file_count
if pattern is None: return file_count
viperlib.snprintf(pattern, dir_len + 16, "%s/*", dir_path)
find_data_size: sizet = w32.win32file.WIN32_FIND_DATAA.__sizeof__()
@@ -83,37 +80,30 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy*,
while True:
fname: str = find_data.cFileName
if fname is None:
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
fname_len: sizet = string.strlen(fname)
if fname_len <= 0:
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
# 跳过 . 和 ..
is_dot: int = 0
if fname_len == 1 and fname[0] == '.':
is_dot = 1
elif fname_len == 2 and fname[0] == '.' and fname[1] == '.':
is_dot = 1
if fname_len == 1 and fname[0] == '.': is_dot = 1
elif fname_len == 2 and fname[0] == '.' and fname[1] == '.': is_dot = 1
if is_dot != 0:
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
# 检查是否是目录
attrs: ULONG = find_data.dwFileAttributes
is_dir: int = 0
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0:
is_dir = 1
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: is_dir = 1
if is_dir != 0:
# 递归扫描子目录
sub_dir: bytes = Utils.JoinPath(dir_path, fname)
if sub_dir is not None:
file_count = _ScanDirForPyFiles(mb, sub_dir, entries, entry_size, file_count, max_files)
stdlib.free(sub_dir)
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
# 检查是否是 .vp 源文件(Viper 源仅 .vp,不再识别 .py)
if fname_len > 3 and Utils.is_vp_at(fname, 0, fname_len) != 0 and file_count < max_files:
@@ -121,8 +111,7 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy*,
if full_path is not None:
sbuf: bytes = Utils.read_file_to_buf(full_path, SRC_BUF_SIZE)
if sbuf is None:
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
# 统一用 IncludesScanner.compute_file_sha1(CRLF→LF 归一化),与 StubMerger 对同一批 App 源文件的口径一致;
# 用原始字节口径会导致 stub/define 符号前缀不一致 → 链接 undefined reference。
@@ -139,8 +128,7 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy*,
stdlib.free(sbuf)
# full_path 不释放:ent.Path 引用它
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
w32.win32file.FindClose(handle)
stdlib.free(pattern)
@@ -161,15 +149,12 @@ def _ScanDirForAsmFiles(mb: memhub.MemBuddy*,
entry_size: sizet,
file_count: int, max_files: int) -> int:
"""递归扫描目录,收集 .s 汇编文件到 entries,返回新的 file_count"""
if dir_path is None or entries is None:
return file_count
if file_count >= max_files:
return file_count
if dir_path is None or entries is None: return file_count
if file_count >= max_files: return file_count
dir_len: sizet = string.strlen(dir_path)
pattern: bytes = stdlib.malloc(dir_len + 16)
if pattern is None:
return file_count
if pattern is None: return file_count
viperlib.snprintf(pattern, dir_len + 16, "%s/*", dir_path)
find_data_size: sizet = w32.win32file.WIN32_FIND_DATAA.__sizeof__()
@@ -188,41 +173,33 @@ def _ScanDirForAsmFiles(mb: memhub.MemBuddy*,
while True:
fname: str = find_data.cFileName
if fname is None:
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
fname_len: sizet = string.strlen(fname)
if fname_len <= 0:
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
# 跳过 . 和 ..
is_dot: int = 0
if fname_len == 1 and fname[0] == '.':
is_dot = 1
elif fname_len == 2 and fname[0] == '.' and fname[1] == '.':
is_dot = 1
if fname_len == 1 and fname[0] == '.': is_dot = 1
elif fname_len == 2 and fname[0] == '.' and fname[1] == '.': is_dot = 1
if is_dot != 0:
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
# 检查是否是目录
attrs: ULONG = find_data.dwFileAttributes
is_dir: int = 0
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0:
is_dir = 1
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: is_dir = 1
if is_dir != 0:
sub_dir: bytes = Utils.JoinPath(dir_path, fname)
if sub_dir is not None:
file_count = _ScanDirForAsmFiles(mb, sub_dir, entries, entry_size, file_count, max_files)
stdlib.free(sub_dir)
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
continue
# 检查是否以 .s 后缀结尾(汇编源文件)
is_asm: int = 0
if fname_len > 2 and fname[fname_len - 2] == '.' and fname[fname_len - 1] == 's':
is_asm = 1
if fname_len > 2 and fname[fname_len - 2] == '.' and fname[fname_len - 1] == 's': is_asm = 1
if is_asm != 0 and file_count < max_files:
full_path: bytes = Utils.JoinPath(dir_path, fname)
if full_path is not None:
@@ -239,8 +216,7 @@ def _ScanDirForAsmFiles(mb: memhub.MemBuddy*,
VLogger.info(f"{fname} (asm sha1={sha1_val})", "project")
# full_path 不释放:ent.Path 引用它
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
w32.win32file.FindClose(handle)
stdlib.free(pattern)
@@ -260,18 +236,15 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy*,
file_count: int,
entry_size: sizet) -> int:
"""mark2 前预注册 App 源文件的泛型类模板,返回处理到的泛型类数量"""
if mb is None or entries is None or file_count <= 0:
return 0
if mb is None or entries is None or file_count <= 0: return 0
handled: int = 0
for i in range(file_count):
ea_pa: uint64 = uint64(entries) + i * entry_size
ent_pa: SrcFileEntry* = (SrcFileEntry*)(ea_pa)
if ent_pa is None or ent_pa.Path is None or ent_pa.Sha1 is None:
continue
if ent_pa is None or ent_pa.Path is None or ent_pa.Sha1 is None: continue
# 读取文件
f_pa: rawptr[fileio.File] = fileio.File(ent_pa.Path, fileio.MODE.R)
if f_pa.closed:
continue
if f_pa.closed: continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f_pa.size() >= SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {SRC_BUF_SIZE} 字节上限, 拒绝读取: {ent_pa.Path}", "PhaseA")
@@ -286,12 +259,10 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy*,
if bytes_read_pa <= 0:
stdlib.free(src_buf_pa)
continue
if bytes_read_pa < SRC_BUF_SIZE:
src_buf_pa[bytes_read_pa] = 0
if bytes_read_pa < SRC_BUF_SIZE: src_buf_pa[bytes_read_pa] = 0
# 解析 AST(只解析,不完整翻译;用永久池使模板节点跨 mark2 reset 存活)
perm_pa: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if perm_pa is None:
perm_pa = mb
if perm_pa is None: perm_pa = mb
lx_pa: ast.Lexer* = ast.new_lexer(perm_pa)
if lx_pa is None:
stdlib.free(src_buf_pa)
@@ -308,32 +279,26 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy*,
cn_pa: sizet = len(ch_pa)
for ci_pa in range(cn_pa):
child_pa: ast.AST* = ch_pa.get(ci_pa)
if child_pa is None:
continue
if child_pa is None: continue
ck_pa: int = child_pa.kind()
if ck_pa == ast.ASTKind.ClassDef:
cd_pa: ast.ClassDef* = ast.ClassDef*(child_pa)
if cd_pa is None or cd_pa.name is None:
continue
if cd_pa is None or cd_pa.name is None: continue
tp_pa: list[str]* = cd_pa.type_params
if tp_pa is None:
continue
if tp_pa is None: continue
GenApi.set_current_file_type(ent_pa.Path)
GenApi._register_generic_template(perm_pa, cd_pa, ent_pa.Sha1)
handled += 1
elif ck_pa == ast.ASTKind.FunctionDef:
fd_pa: ast.FunctionDef* = ast.FunctionDef*(child_pa)
if fd_pa is None or fd_pa.name is None:
continue
if fd_pa is None or fd_pa.name is None: continue
tf_pa: list[str]* = fd_pa.type_params
if tf_pa is None:
continue
if tf_pa is None: continue
GenApi.set_current_file_type(ent_pa.Path)
GenApi._register_generic_func_template(perm_pa, fd_pa, ent_pa.Sha1)
handled += 1
stdlib.free(src_buf_pa)
if handled > 0:
VLogger.info(f"mark2 前预注册 {handled} 个 App 泛型模板(类+函数)", "PhaseA")
if handled > 0: VLogger.info(f"mark2 前预注册 {handled} 个 App 泛型模板(类+函数)", "PhaseA")
return handled
@@ -363,12 +328,9 @@ def _PhaseADiagInit(max_count: sizet) -> None:
_pa_diag_sha1 = stdlib.malloc(max_count * 17)
_pa_diag_deps = stdlib.malloc(max_count * PA_DEPS_MAX_STR)
_pa_diag_miss = stdlib.malloc(max_count * PA_MISS_MAX_STR)
if _pa_diag_sha1 is not None:
string.memset(_pa_diag_sha1, 0, max_count * 17)
if _pa_diag_deps is not None:
string.memset(_pa_diag_deps, 0, max_count * PA_DEPS_MAX_STR)
if _pa_diag_miss is not None:
string.memset(_pa_diag_miss, 0, max_count * PA_MISS_MAX_STR)
if _pa_diag_sha1 is not None: string.memset(_pa_diag_sha1, 0, max_count * 17)
if _pa_diag_deps is not None: string.memset(_pa_diag_deps, 0, max_count * PA_DEPS_MAX_STR)
if _pa_diag_miss is not None: string.memset(_pa_diag_miss, 0, max_count * PA_MISS_MAX_STR)
def _PhaseADiagFree() -> None:
@@ -390,10 +352,8 @@ def _PhaseADiagFree() -> None:
def _PhaseADiagRecord(sha1: str, deps: str, miss: str) -> None:
"""记录/更新单文件诊断:sha1 → 依赖串 + 缺失明细(None 表示不更新该字段)"""
global _pa_diag_count
if _pa_diag_sha1 is None or _pa_diag_deps is None or sha1 is None:
return
if string.strlen(sha1) < 16:
return
if _pa_diag_sha1 is None or _pa_diag_deps is None or sha1 is None: return
if string.strlen(sha1) < 16: return
for i in range(_pa_diag_count):
es: bytes = _pa_diag_sha1 + sizet(i) * 17
if string.strcmp(es, sha1) == 0:
@@ -402,14 +362,12 @@ def _PhaseADiagRecord(sha1: str, deps: str, miss: str) -> None:
string.strncpy(maddr, miss, PA_MISS_MAX_STR - 1)
maddr[PA_MISS_MAX_STR - 1] = 0
return
if _pa_diag_count >= _pa_diag_max:
return
if _pa_diag_count >= _pa_diag_max: return
saddr: bytes = _pa_diag_sha1 + sizet(_pa_diag_count) * 17
string.strncpy(saddr, sha1, 16)
saddr[16] = 0
daddr: bytes = _pa_diag_deps + sizet(_pa_diag_count) * PA_DEPS_MAX_STR
if deps is not None:
string.strncpy(daddr, deps, PA_DEPS_MAX_STR - 1)
if deps is not None: string.strncpy(daddr, deps, PA_DEPS_MAX_STR - 1)
daddr[PA_DEPS_MAX_STR - 1] = 0
if miss is not None and _pa_diag_miss is not None:
maddr2: bytes = _pa_diag_miss + sizet(_pa_diag_count) * PA_MISS_MAX_STR
@@ -420,16 +378,14 @@ def _PhaseADiagRecord(sha1: str, deps: str, miss: str) -> None:
def _PhaseADiagDepsLookup(sha1: str) -> str:
"""按 sha1 查持久化依赖串(stdlib.malloc 副本,调用者 free;未命中 None)"""
if _pa_diag_sha1 is None or _pa_diag_deps is None or sha1 is None:
return None
if _pa_diag_sha1 is None or _pa_diag_deps is None or sha1 is None: return None
for i in range(_pa_diag_count):
es: bytes = _pa_diag_sha1 + sizet(i) * 17
if string.strcmp(es, sha1) == 0:
src: bytes = _pa_diag_deps + sizet(i) * PA_DEPS_MAX_STR
ln: sizet = string.strlen(src)
out: bytes = stdlib.malloc(ln + 1)
if out is None:
return None
if out is None: return None
string.strcpy(out, src)
return out
return None
@@ -437,18 +393,15 @@ def _PhaseADiagDepsLookup(sha1: str) -> str:
def _PhaseADiagMissLookup(sha1: str) -> str:
"""按 sha1 查持久化缺失明细(stdlib.malloc 副本,调用者 free;未命中/空 None)"""
if _pa_diag_sha1 is None or _pa_diag_miss is None or sha1 is None:
return None
if _pa_diag_sha1 is None or _pa_diag_miss is None or sha1 is None: return None
for i in range(_pa_diag_count):
es: bytes = _pa_diag_sha1 + sizet(i) * 17
if string.strcmp(es, sha1) == 0:
src: bytes = _pa_diag_miss + sizet(i) * PA_MISS_MAX_STR
ln: sizet = string.strlen(src)
if ln == 0:
return None
if ln == 0: return None
out: bytes = stdlib.malloc(ln + 1)
if out is None:
return None
if out is None: return None
string.strcpy(out, src)
return out
return None
@@ -456,11 +409,9 @@ def _PhaseADiagMissLookup(sha1: str) -> str:
def _PhaseATextLLExists(temp_dir: str, td_len: sizet, sha1: str) -> int:
"""探测 {temp_dir}/{sha1前2位}/{sha1}.text.ll 是否存在,1=存在"""
if temp_dir is None or td_len == 0 or sha1 is None or string.strlen(sha1) < 2:
return 0
if temp_dir is None or td_len == 0 or sha1 is None or string.strlen(sha1) < 2: return 0
tpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "text.ll")
if tpath is None:
return 0
if tpath is None: return 0
tf: rawptr[fileio.File] = fileio.File(tpath, fileio.MODE.R)
exists: int = 0
if not tf.closed:
@@ -472,11 +423,9 @@ def _PhaseATextLLExists(temp_dir: str, td_len: sizet, sha1: str) -> int:
def _PhaseAResolveDepSha1(mod_name: str) -> str:
"""依赖模块名 → SHA1(精确 → 后缀兜底),未命中 None"""
if mod_name is None:
return None
if mod_name is None: return None
s1: str = StubMergerStore.LookupSha1ByModName(mod_name)
if s1 is not None:
return s1
if s1 is not None: return s1
return StubMergerStore.LookupSha1ByModNameSuffix(mod_name)
@@ -484,8 +433,7 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str,
depth: int, prefix: bytes) -> None:
"""递归打印依赖子图;[MISS]=缺 text.ll(下钻整链),[OK]=就绪(仅探测 1 层)"""
deps: bytes = None
if sha1 is not None:
deps = _PhaseADiagDepsLookup(sha1)
if sha1 is not None: deps = _PhaseADiagDepsLookup(sha1)
if deps is None and sha1 is not None:
# 回退:读取 {temp_dir}/{前缀}/{sha1}.deps.txt(Phase1 已为 includes 写盘)
dpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "deps.txt")
@@ -495,85 +443,64 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str,
deps = stdlib.malloc(PA_DEPS_MAX_STR)
if deps is not None:
rl: int64 = df.read_all(deps, PA_DEPS_MAX_STR)
if rl < 0:
rl = 0
if rl < 0: rl = 0
# read_all 的容量参数即缓冲区容量(含终止符):传真实容量 MAX 时最多读 MAX-1 字节,故 rl 恒 <= MAX-1,在界内直接写终止符即可。
deps[rl] = 0
df.close()
stdlib.free(dpath)
if deps is None or deps[0] == 0:
if deps is not None:
stdlib.free(deps)
if deps is not None: stdlib.free(deps)
return
dl: sizet = string.strlen(deps)
token_count: int = 0
pos: sizet = 0
while pos < dl:
while pos < dl and deps[pos] == ' ':
pos += 1
if pos >= dl:
break
while pos < dl and deps[pos] == ' ': pos += 1
if pos >= dl: break
token_count += 1
while pos < dl and deps[pos] != ' ':
pos += 1
while pos < dl and deps[pos] != ' ': pos += 1
pos = 0
idx: int = 0
while pos < dl:
while pos < dl and deps[pos] == ' ':
pos += 1
if pos >= dl:
break
while pos < dl and deps[pos] == ' ': pos += 1
if pos >= dl: break
ts: sizet = pos
while pos < dl and deps[pos] != ' ':
pos += 1
while pos < dl and deps[pos] != ' ': pos += 1
tl: sizet = pos - ts
nm: bytes = stdlib.malloc(tl + 1)
if nm is not None:
string.strncpy(nm, deps + ts, tl)
nm[tl] = 0
is_last: int = 0
if idx + 1 >= token_count:
is_last = 1
if idx + 1 >= token_count: is_last = 1
builtin: int = 0
if string.strcmp(nm, "t") == 0 or string.strcmp(nm, "c") == 0:
builtin = 1
if string.strcmp(nm, "t") == 0 or string.strcmp(nm, "c") == 0: builtin = 1
dep_sha1: str = _PhaseAResolveDepSha1(nm)
txt_ok: int = 0
if dep_sha1 is not None:
txt_ok = _PhaseATextLLExists(temp_dir, td_len, dep_sha1)
if dep_sha1 is not None: txt_ok = _PhaseATextLLExists(temp_dir, td_len, dep_sha1)
# 分支字符内联(不返回字符串字面量:避免 TPV 对字面量返回的
# 栈临时指针悬垂 → printf %s 崩溃,图输出被吞)
br_1: str = "|-- "
br_2: str = "`-- "
if builtin != 0:
if is_last != 0:
stdio.printf("%s%s%s (builtin)\n", prefix, br_2, nm)
else:
stdio.printf("%s%s%s (builtin)\n", prefix, br_1, nm)
if is_last != 0: stdio.printf("%s%s%s (builtin)\n", prefix, br_2, nm)
else: stdio.printf("%s%s%s (builtin)\n", prefix, br_1, nm)
elif dep_sha1 is None:
if is_last != 0:
stdio.printf("%s%s%s (unresolved)\n", prefix, br_2, nm)
else:
stdio.printf("%s%s%s (unresolved)\n", prefix, br_1, nm)
if is_last != 0: stdio.printf("%s%s%s (unresolved)\n", prefix, br_2, nm)
else: stdio.printf("%s%s%s (unresolved)\n", prefix, br_1, nm)
elif txt_ok != 0:
if is_last != 0:
stdio.printf("%s%s%s [OK]\n", prefix, br_2, nm)
else:
stdio.printf("%s%s%s [OK]\n", prefix, br_1, nm)
if is_last != 0: stdio.printf("%s%s%s [OK]\n", prefix, br_2, nm)
else: stdio.printf("%s%s%s [OK]\n", prefix, br_1, nm)
else:
if is_last != 0:
stdio.printf("%s%s%s [MISS] text.ll=%s\n", prefix, br_2, nm, dep_sha1)
else:
stdio.printf("%s%s%s [MISS] text.ll=%s\n", prefix, br_1, nm, dep_sha1)
if is_last != 0: stdio.printf("%s%s%s [MISS] text.ll=%s\n", prefix, br_2, nm, dep_sha1)
else: stdio.printf("%s%s%s [MISS] text.ll=%s\n", prefix, br_1, nm, dep_sha1)
# 下钻:MISS 全链(至 PA_GRAPH_DEPTH_MAX),OK 仅探测 1 层(PA_GRAPH_OK_PROBE)
can_expand: int = 0
if dep_sha1 is not None and builtin == 0:
if txt_ok == 0:
if depth + 1 < PA_GRAPH_DEPTH_MAX:
can_expand = 1
if depth + 1 < PA_GRAPH_DEPTH_MAX: can_expand = 1
else:
if depth + 1 < PA_GRAPH_OK_PROBE:
can_expand = 1
if depth + 1 < PA_GRAPH_OK_PROBE: can_expand = 1
if can_expand != 0:
# +5:strcat 追加 "| "/" "(4 字符)+ NUL;strlen+4 会溢出 1 字节
# 破坏堆元数据 → 后续 malloc/free 崩溃,图打印中断且 stdout 缓冲丢失
@@ -581,10 +508,8 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str,
new_prefix: bytes = stdlib.malloc(npl)
if new_prefix is not None:
string.strcpy(new_prefix, prefix)
if is_last != 0:
string.strcat(new_prefix, " ")
else:
string.strcat(new_prefix, "| ")
if is_last != 0: string.strcat(new_prefix, " ")
else: string.strcat(new_prefix, "| ")
_PhaseADumpNode(temp_dir, td_len, dep_sha1, depth + 1, new_prefix)
stdlib.free(new_prefix)
stdlib.free(nm)
@@ -604,8 +529,7 @@ def _PhaseADumpFailGraph(temp_dir: str, td_len: sizet, path: str, sha1: str) ->
stdio.fflush(None)
miss: bytes = _PhaseADiagMissLookup(sha1)
if miss is not None:
if miss[0] != 0:
stdio.printf("[PhaseA-Fail] pending reason: %s\n", miss)
if miss[0] != 0: stdio.printf("[PhaseA-Fail] pending reason: %s\n", miss)
stdlib.free(miss)
stdio.fflush(None)
stdio.printf("[PhaseA-Fail] dep graph ([MISS]=missing text.ll, [OK]=ready, depth<=%d):\n",
@@ -643,8 +567,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# === 1. 递归扫描 source_dir 下的 .vp/.py 文件(包括子目录)===
entry_size: sizet = SrcFileEntry.__sizeof__()
entries: SrcFileEntry* = stdlib.malloc(MAX_SRC_FILES * entry_size)
if entries is None:
return 1
if entries is None: return 1
string.memset(entries, 0, MAX_SRC_FILES * entry_size)
file_count: int = _ScanDirForPyFiles(mb, source_dir, entries, entry_size, 0, MAX_SRC_FILES)
@@ -657,8 +580,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
return 1
string.memset(asm_entries, 0, MAX_SRC_FILES * entry_size)
asm_file_count: int = _ScanDirForAsmFiles(mb, source_dir, asm_entries, entry_size, 0, MAX_SRC_FILES)
if asm_file_count > 0:
VLogger.info(f"汇编文件 {asm_file_count} 个(llvm-mc 汇编参与链接)", "project")
if asm_file_count > 0: VLogger.info(f"汇编文件 {asm_file_count} 个(llvm-mc 汇编参与链接)", "project")
if file_count == 0:
stdlib.free(entries)
@@ -690,16 +612,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
if includes_dir is not None:
if StubMergerStore.GetSha1StoreCount() == 0:
rs_05: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17)
if rs_05 is None:
VLogger.error("reachable_set 分配失败,无法填充 Sha1Store", "project")
if rs_05 is None: VLogger.error("reachable_set 分配失败,无法填充 Sha1Store", "project")
else:
string.memset(rs_05, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
rc_05: int = StubMergerBuild._BuildReachableSha1Set(
mb, source_dir, temp_dir, rs_05, None, includes_dir)
if rc_05 < 0:
VLogger.error("依赖图扫描失败,无法填充 Sha1Store", "project")
elif rc_05 > 0:
VLogger.info(f"Phase2 依赖图按需填充 Sha1Store: {rc_05} 个", "project")
if rc_05 < 0: VLogger.error("依赖图扫描失败,无法填充 Sha1Store", "project")
elif rc_05 > 0: VLogger.info(f"Phase2 依赖图按需填充 Sha1Store: {rc_05} 个", "project")
stdlib.free(rs_05)
# 追加 App 源文件 SHA1 到 _sha1_map.txt(人类可读输出,程序内部不读取此文件)
@@ -717,27 +636,23 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
for i in range(file_count):
ea_am: uint64 = uint64(entries) + i * entry_size
ent_am: SrcFileEntry* = (SrcFileEntry*)(ea_am)
if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None:
continue
if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None: continue
# 计算相对路径(去除 source_dir 前缀,保留目录结构)
p_str: str = ent_am.Path
p_len: sizet = string.strlen(p_str)
rel_path_am: str = p_str
if p_len > src_dir_len_am + 1:
if string.strncmp(p_str, source_dir, src_dir_len_am) == 0:
rel_path_am = p_str + src_dir_len_am + 1
if string.strncmp(p_str, source_dir, src_dir_len_am) == 0: rel_path_am = p_str + src_dir_len_am + 1
# 同步追加到内存存储器(供跨模块 CDefine 查找)
# rel_path_am 格式如 "lib/core/StubMerger.vp",与 _sha1_map.txt 一致
if StubMergerStore.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) != 0:
VLogger.error(f"AppendToSha1MapStore 失败: {rel_path_am}", "project")
if StubMergerStore.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) != 0: VLogger.error(f"AppendToSha1MapStore 失败: {rel_path_am}", "project")
app_filled += 1
# 写入 _sha1_map.txt(人类可读输出)
if not mf.closed and line_am is not None:
viperlib.snprintf(line_am, 512, "%s:%s\n", ent_am.Sha1, rel_path_am)
ll_am: sizet = string.strlen(line_am)
mf.write(line_am, ll_am)
if line_am is not None:
stdlib.free(line_am)
if line_am is not None: stdlib.free(line_am)
if not mf.closed:
mf.close()
VLogger.info(f"已追加 {app_filled} 个 App 文件到 _sha1_map.txt 和内存存储器", "project")
@@ -802,8 +717,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | 文件读取失败或为空", "PhaseA")
continue
if bytes_read_vp2 < SRC_BUF_SIZE:
src_buf_vp2[bytes_read_vp2] = 0
if bytes_read_vp2 < SRC_BUF_SIZE: src_buf_vp2[bytes_read_vp2] = 0
lx_vp2: ast.Lexer* = ast.new_lexer(mb)
if lx_vp2 is None:
stdlib.free(src_buf_vp2)
@@ -876,15 +790,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# 返回类型默认推断成 i32,调用点报误导性的「找不到模块 SHA1」)。declare_only=1 时 import 的 SHA1 检查跳过,故预注册失败只可能是文件自身无法翻译。
# 必须跳过 App 文件:Sha1Store 里含 App 源文件 SHA1,但其预注册由 Phase A-pre 负责(App 是严格模式且此阶段依赖未就绪,在此预注册必然失败误报)。
inc_fail_flags: bytes = stdlib.malloc(store_count_inc_pre)
if inc_fail_flags is not None:
string.memset(inc_fail_flags, 1, store_count_inc_pre)
if inc_fail_flags is not None: string.memset(inc_fail_flags, 1, store_count_inc_pre)
for pass_inc_i in range(PHASE_A_INC_MAX_PASSES):
struct_count_before_inc: int = GenApi.get_struct_count()
for si_inc in range(store_count_inc_pre):
inc_sha1_pre: str = store_sha1_inc_pre + sizet(si_inc) * 17
inc_rel_pre: str = store_rel_inc_pre + sizet(si_inc) * StubMerger.MAX_REL_PATH_LEN
if inc_rel_pre is None or inc_rel_pre[0] == '\0':
continue
if inc_rel_pre is None or inc_rel_pre[0] == '\0': continue
# 跳过 App 源文件(main.vp 等):由 Phase A-pre 预注册,不算失败
is_app_file_inc: int = 0
for i_app_inc in range(file_count):
@@ -899,12 +811,10 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
rel_len_pre: sizet = string.strlen(inc_rel_pre)
# 所属 includes 根目录(多目录逐条取自 IncDir 数组,空串回退 includes_dir)
incdir_pre: str = store_incdir_inc_pre + sizet(si_inc) * StubMerger.MAX_INC_DIR_LEN
if incdir_pre is None or incdir_pre[0] == '\0':
incdir_pre = includes_dir
if incdir_pre is None or incdir_pre[0] == '\0': incdir_pre = includes_dir
incdir_len_pre: sizet = string.strlen(incdir_pre)
full_path_pre: bytes = stdlib.malloc(incdir_len_pre + rel_len_pre + 2)
if full_path_pre is None:
continue
if full_path_pre is None: continue
viperlib.snprintf(full_path_pre, incdir_len_pre + rel_len_pre + 2,
"%s/%s", incdir_pre, inc_rel_pre)
pkg_inc_pre: str = GenApi.compute_package_from_relpath(mb, inc_rel_pre)
@@ -915,8 +825,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# 记录本文件依赖(供收敛失败依赖图诊断;stdlib 表跨 reset 存活)
_PhaseADiagRecord(inc_sha1_pre, tr_inc_pre._imported_modules, None)
# 预注册成功:清除失败标记
if inc_fail_flags is not None:
inc_fail_flags[si_inc] = 0
if inc_fail_flags is not None: inc_fail_flags[si_inc] = 0
# 依赖填充只在第一遍执行(PopulateIncludesDeps 内部有去重)
# 注意:必须在 reset_to(mark2) 之前调用——内部深拷贝到 stdlib
if pass_inc_i == 0 and tr_inc_pre._imported_modules is not None and StubMergerStore.PopulateIncludesDeps(
@@ -927,8 +836,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
mb.reset_to(mark2)
struct_count_after_inc: int = GenApi.get_struct_count()
# 收敛检查:本遍没有新结构体注册 → 所有类已注册
if struct_count_after_inc == struct_count_before_inc:
break
if struct_count_after_inc == struct_count_before_inc: break
# 多遍后仍预注册失败的 includes → 报错终止(不允许降级)
if inc_fail_flags is not None:
for si_inc_f in range(store_count_inc_pre):
@@ -949,8 +857,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
for i in range(file_count):
ea_pre: uint64 = uint64(entries) + i * entry_size
ent_pre: SrcFileEntry* = (SrcFileEntry*)(ea_pre)
if ent_pre is None or ent_pre.Path is None:
continue
if ent_pre is None or ent_pre.Path is None: continue
pkg_pre: str = None
if string.strlen(ent_pre.Path) > src_dir_len_pre + 1:
rel_path_pre: str = ent_pre.Path + src_dir_len_pre + 1
@@ -961,14 +868,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# 解析出的真定义前缀分裂 → 结构体布局不兼容,见 Translator 字段说明)。
tr_pre: rawptr[HandlesTranslator.Translator] = BuildPipeline.TranslateFileGetTrans(
mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1, None, 1)
if tr_pre is None:
VLogger.error(f"预注册失败: {ent_pre.Path}", "PhaseA")
if tr_pre is None: VLogger.error(f"预注册失败: {ent_pre.Path}", "PhaseA")
# OOM 清理:回收本 App 文件预注册翻译的瞬时数据
mb.reset_to(mark2)
struct_count_after: int = GenApi.get_struct_count()
# 收敛检查:本遍没有新结构体注册 → 所有类已注册
if struct_count_after == struct_count_before:
break
if struct_count_after == struct_count_before: break
VLogger.info("预注册完成", "PhaseA")
# Phase A IR 缓冲:1MB 不够,ast/parser.vp 的 text.ll 超 1MB 被截断 →
@@ -987,8 +892,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# 声明已在 if 块外,此处仅赋值
app_deps_buf = stdlib.malloc(APP_DEPS_BUF_SIZE)
app_deps_len = 0
if app_deps_buf is not None:
app_deps_buf[0] = '\0'
if app_deps_buf is not None: app_deps_buf[0] = '\0'
# Phase A 多遍重试:同项目多源文件时,main.vp 可能先于其 import 的
# mbuddytest.vp 翻译(文件系统顺序);翻译失败(语法/解析错误)的文件
@@ -1003,8 +907,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
for pass_a in range(PHASE_A_MAX_PASSES):
completed_this_pass: int = 0
for i in range(file_count):
if done_flags[i] != 0:
continue
if done_flags[i] != 0: continue
ea: uint64 = uint64(entries) + i * entry_size
ent: SrcFileEntry* = (SrcFileEntry*)(ea)
# 计时点 0:本文件处理开始(translate 段起点)
@@ -1038,16 +941,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
im_pos_a: sizet = 0
while im_pos_a < im_len_a:
im_start_a: sizet = im_pos_a
while im_pos_a < im_len_a and tr_a._imported_modules[im_pos_a] != ' ':
im_pos_a += 1
while im_pos_a < im_len_a and tr_a._imported_modules[im_pos_a] != ' ': im_pos_a += 1
im_mod_len: sizet = im_pos_a - im_start_a
if im_pos_a < im_len_a:
im_pos_a += 1
if im_mod_len == 0:
continue
if im_pos_a < im_len_a: im_pos_a += 1
if im_mod_len == 0: continue
im_mod_buf: bytes = stdlib.malloc(im_mod_len + 1)
if im_mod_buf is None:
continue
if im_mod_buf is None: continue
string.strncpy(im_mod_buf, tr_a._imported_modules + im_start_a, im_mod_len)
im_mod_buf[im_mod_len] = '\0'
# 已在 app_deps 中则跳过(词边界精确匹配)
@@ -1064,8 +963,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
stdlib.free(im_mod_buf)
# dump stub IR (declarations only)
stub_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE)
if stub_buf_a is None:
VLogger.error("stub 缓冲区分配失败", "PhaseA")
if stub_buf_a is None: VLogger.error("stub 缓冲区分配失败", "PhaseA")
tr_a.dump_ir(stub_buf_a, PHASE_A_IR_SIZE, mlirlite.OUTPUT_STUB)
stub_len_a: sizet = string.strlen(stub_buf_a)
@@ -1081,8 +979,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# dump text IR (definitions only)
text_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE)
if text_buf_a is None:
VLogger.error("text 缓冲区分配失败", "PhaseA")
if text_buf_a is None: VLogger.error("text 缓冲区分配失败", "PhaseA")
tr_a.dump_ir(text_buf_a, PHASE_A_IR_SIZE, mlirlite.OUTPUT_TEXT)
text_len_a: sizet = string.strlen(text_buf_a)
@@ -1101,8 +998,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# save {sha1}.full.ll(在线 FULL 产物:模型在线时的自足单模块 IR)
full_buf_a: bytes = stdlib.malloc(PHASE_A_FULL_SIZE)
if full_buf_a is None:
VLogger.error("full.ll 缓冲区分配失败", "PhaseA")
if full_buf_a is None: VLogger.error("full.ll 缓冲区分配失败", "PhaseA")
tr_a.dump_ir(full_buf_a, PHASE_A_FULL_SIZE, mlirlite.OUTPUT_FULL)
full_len_a: sizet = string.strlen(full_buf_a)
full_path_a: str = StubMergerCollect._sliced_path(temp_dir, td_len_pa, ent.Sha1, "full.ll")
@@ -1147,8 +1043,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
if done_flags[i_ck] == 0:
all_done = 0
break
if all_done != 0:
break
if all_done != 0: break
if completed_this_pass == 0:
# 无进展:剩余文件无法完成(真实编译错误),终止并列出未完成文件。每个失败文件附带依赖图诊断(_PhaseADumpFailGraph):pending 明细 + 依赖树。
# VLogger.error 内部 sys.exit(1),必须放在所有图形输出之后(否则进程在 ERROR 处即退出,图永远打不出)。
@@ -1158,8 +1053,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
ea_err: uint64 = uint64(entries) + i_err * entry_size
ent_err: SrcFileEntry* = (SrcFileEntry*)(ea_err)
if ent_err is not None and ent_err.Path is not None:
if ent_err.Sha1 is not None:
_PhaseADumpFailGraph(temp_dir, td_len_fail, ent_err.Path, ent_err.Sha1)
if ent_err.Sha1 is not None: _PhaseADumpFailGraph(temp_dir, td_len_fail, ent_err.Path, ent_err.Sha1)
stdlib.free(done_flags)
_PhaseADiagFree()
VLogger.error("Phase A 多遍收敛失败,终止编译", "PhaseA")
@@ -1171,8 +1065,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
if do_phase2 == 0:
VLogger.info("Phase A 完成(仅 stub 生成)", "project")
if app_deps_buf is not None:
stdlib.free(app_deps_buf)
if app_deps_buf is not None: stdlib.free(app_deps_buf)
stdlib.free(entries)
return 0
@@ -1186,15 +1079,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
reachable_count: int = 0
OBJ_PATHS_SIZE: sizet = 65536
obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE)
if obj_paths is None:
return 1
if obj_paths is None: return 1
obj_paths[0] = '\0'
obj_pos: sizet = 0
# main_obj_path: 存放定义了用户 main 的 .obj 路径(链接时放在最前面,
# 避免 --allow-multiple-definition 选择了其他模块的 wrapper main)
main_obj_path: bytes = stdlib.malloc(512)
if main_obj_path is None:
return 1
if main_obj_path is None: return 1
main_obj_path[0] = '\0'
compiled_count: int = 0
@@ -1212,8 +1103,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
for i in range(file_count):
ea: uint64 = uint64(entries) + i * entry_size
ent: SrcFileEntry* = (SrcFileEntry*)(ea)
if ent is None or ent.Path is None or ent.Sha1 is None:
continue
if ent is None or ent.Path is None or ent.Sha1 is None: continue
# 单文件三步计时起点(GetTickCount64,ms)
t_pb0: uint64 = w32.win32base.GetTickCount64()
# 读在线 FULL 产物 {sha1}.full.ll → 完整 IR(取代 stub + 依赖 stub/text 合并)
@@ -1258,10 +1148,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# 构造 .obj 路径,检测是否是 main 模块(test_main.vp 或 main.vp,不再识别 .py)
od_len: sizet = string.strlen(output_dir)
is_main_mod: int = 0
if string.strstr(ent.Path, "test_main.vp") is not None:
is_main_mod = 1
elif string.strstr(ent.Path, "main.vp") is not None:
is_main_mod = 1
if string.strstr(ent.Path, "test_main.vp") is not None: is_main_mod = 1
elif string.strstr(ent.Path, "main.vp") is not None: is_main_mod = 1
# 切片路径: output_dir/{sha1前缀}/{sha1}.obj
obj_path_sliced: str = StubMergerCollect._sliced_path(output_dir, od_len, ent.Sha1, "obj")
@@ -1269,10 +1157,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
op_sliced_len: sizet = string.strlen(obj_path_sliced)
if is_main_mod != 0:
# main 模块: 复制到 main_obj_path(确保链接时 main 在最前)
if op_sliced_len < 512:
string.strcpy(main_obj_path, obj_path_sliced)
else:
VLogger.error("main_obj_path 缓冲区不足,终止编译", "PhaseB")
if op_sliced_len < 512: string.strcpy(main_obj_path, obj_path_sliced)
else: VLogger.error("main_obj_path 缓冲区不足,终止编译", "PhaseB")
else:
# 其他模块: 追加到 obj_paths
if obj_pos + op_sliced_len + 2 < OBJ_PATHS_SIZE:
@@ -1282,8 +1168,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
string.strcpy(obj_paths + obj_pos, obj_path_sliced)
obj_pos += op_sliced_len
obj_paths[obj_pos] = '\0'
else:
VLogger.error(".obj 路径缓冲区不足", "PhaseB")
else: VLogger.error(".obj 路径缓冲区不足", "PhaseB")
stdlib.free(obj_path_sliced)
# 并行执行全部 llc(分批 LLC_MAX_THREADS 线程)
@@ -1308,8 +1193,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
for ai_as in range(asm_file_count):
ea_as: uint64 = uint64(asm_entries) + ai_as * entry_size
ent_as: SrcFileEntry* = (SrcFileEntry*)(ea_as)
if ent_as is None or ent_as.Path is None or ent_as.Sha1 is None:
continue
if ent_as is None or ent_as.Path is None or ent_as.Sha1 is None: continue
aret_as: int = BuildPipeline.asm_to_obj(
ent_as.Path, ent_as.Sha1, output_dir, Config.TargetTriple)
if aret_as != 0:
@@ -1418,8 +1302,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
if string.strcmp(ent_ap.Sha1, inc_sha1_mi) == 0:
app_skip_mi = 1
break
if app_skip_mi != 0:
continue
if app_skip_mi != 0: continue
# 检查 .obj 是否已存在于 includes.binary(切片子目录)
ibd_len_mi: sizet = string.strlen(includes_binary_dir)
check_pat_mi: str = StubMergerCollect._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj")
@@ -1432,8 +1315,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
w32.win32file.FindClose(check_h_mi)
obj_exists_mi = 1
if obj_exists_mi != 0:
if check_pat_mi is not None:
stdlib.free(check_pat_mi)
if check_pat_mi is not None: stdlib.free(check_pat_mi)
stdlib.free(check_fd_mi)
continue
@@ -1441,14 +1323,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# 构造源文件路径: {所属 includes 根目录}/{rel_path}(多目录逐条取 IncDir,空串回退 includes_dir)
rel_path_len_mi: sizet = string.strlen(inc_rel_mi)
incdir_mi: str = store_incdir_arr_mi + sizet(ti) * StubMerger.MAX_INC_DIR_LEN
if incdir_mi is None or incdir_mi[0] == '\0':
incdir_mi = includes_dir
if incdir_mi is None or incdir_mi[0] == '\0': incdir_mi = includes_dir
inc_dir_len_mi: sizet = string.strlen(incdir_mi)
src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1)
if src_fp_mi is None:
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
if check_fd_mi is not None: stdlib.free(check_fd_mi)
VLogger.error("includes 源路径分配失败,终止编译", "PhaseB+")
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1, "%s/%s", incdir_mi, inc_rel_mi)
@@ -1461,16 +1341,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
# 只跳过声明文件(is_decl_mi == 1);meta 不存在时继续,让后续翻译逻辑处理
if is_decl_mi == 1:
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
if check_fd_mi is not None: stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
continue
# 尝试读在线 FULL 产物 {sha1}.full.ll(应已由 Phase1/PhaseA 生成)
inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE)
inc_combined_len_mi: sizet = 0
if inc_combined_mi is not None:
inc_combined_len_mi = StubMergerBuild.LoadFullIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
if inc_combined_mi is not None: inc_combined_len_mi = StubMergerBuild.LoadFullIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
# 如果 full.ll 不存在,翻译源文件并保存 stub/text/meta/full,然后重试
if inc_combined_len_mi == 0 and inc_combined_mi is not None:
@@ -1515,8 +1393,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
StubMergerIR.WriteModuleMeta(tr_mi.Module, temp_dir, td_len_mi, inc_sha1_mi)
# save {sha1}.full.ll(在线 FULL 产物)
inc_full_buf: bytes = stdlib.malloc(PBP_FULL_SIZE)
if inc_full_buf is None:
VLogger.error("full.ll 缓冲区分配失败", "PhaseB+")
if inc_full_buf is None: VLogger.error("full.ll 缓冲区分配失败", "PhaseB+")
tr_mi.dump_ir(inc_full_buf, PBP_FULL_SIZE, mlirlite.OUTPUT_FULL)
inc_full_len: sizet = string.strlen(inc_full_buf)
inc_full_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "full.ll")
@@ -1566,11 +1443,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
df_diag_t.close()
stdlib.free(diag_text)
VLogger.error(f"读 full.ll 失败: {src_fp_mi} (full={diag_full_ex} stub={diag_stub_ex} text={diag_text_ex} is_decl={is_decl_mi})", "PhaseB+")
if inc_combined_mi is not None:
stdlib.free(inc_combined_mi)
if inc_combined_mi is not None: stdlib.free(inc_combined_mi)
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
if check_fd_mi is not None: stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
continue
@@ -1601,26 +1476,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
string.strcpy(obj_paths + obj_pos, inc_obj_sliced)
obj_pos += inc_obj_len
obj_paths[obj_pos] = '\0'
else:
VLogger.error("includes .obj 路径缓冲区溢出,终止编译", "PhaseB+")
else: VLogger.error("includes .obj 路径缓冲区溢出,终止编译", "PhaseB+")
stdlib.free(inc_obj_sliced)
# 释放本次迭代的临时内存
if check_pat_mi is not None:
stdlib.free(check_pat_mi)
if check_pat_mi is not None: stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
if check_fd_mi is not None: stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
# 释放拓扑排序索引与局部映射副本
if topo_idx is not None:
stdlib.free(topo_idx)
if store_sha1_arr_mi is not None:
stdlib.free(store_sha1_arr_mi)
if store_mod_arr_mi is not None:
stdlib.free(store_mod_arr_mi)
if topo_idx is not None: stdlib.free(topo_idx)
if store_sha1_arr_mi is not None: stdlib.free(store_sha1_arr_mi)
if store_mod_arr_mi is not None: stdlib.free(store_mod_arr_mi)
VLogger.info(f"编译缺失 includes: {inc_compiled} 个", "PhaseB+")
@@ -1635,8 +1504,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
od_len2: sizet = string.strlen(output_dir)
lo_len: sizet = string.strlen(linker_output)
exe_path: bytes = stdlib.malloc(od_len2 + lo_len + 2)
if exe_path is not None:
viperlib.snprintf(exe_path, od_len2 + lo_len + 2, "%s/%s", output_dir, linker_output)
if exe_path is not None: viperlib.snprintf(exe_path, od_len2 + lo_len + 2, "%s/%s", output_dir, linker_output)
else:
exe_path = linker_output
VLogger.error("exe_path 分配失败,终止编译", "PhaseC")
@@ -1674,8 +1542,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
ri_pc: int
for ri_pc in range(reachable_count):
rsha1_pc: str = reachable_set + sizet(ri_pc) * 17
if rsha1_pc[0] == '\0':
continue
if rsha1_pc[0] == '\0': continue
# 跳过 App 源文件:其 .obj 已入 final_obj_paths,若 includes_binary 残留同 sha1 的 .obj 被收集进来会与 output obj 同符号 → 链接 duplicate symbol。
app_skip_pc: int = 0
for ai_pc in range(file_count):
@@ -1685,8 +1552,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
if string.strcmp(ent_pc.Sha1, rsha1_pc) == 0:
app_skip_pc = 1
break
if app_skip_pc != 0:
continue
if app_skip_pc != 0: continue
obj_path_pc: str = StubMergerCollect._sliced_path(includes_binary_dir, ibd_len_pc, rsha1_pc, "obj")
if obj_path_pc is not None:
# 检查 .obj 文件是否存在
@@ -1716,8 +1582,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
string.strcpy(final_obj_paths + fop_pos, obj_path_pc)
fop_pos += op_len_p2
final_obj_paths[fop_pos] = '\0'
elif StubMergerIR.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1:
VLogger.error(f"可达模块 .obj 缺失: {obj_path_pc}", "PhaseC")
elif StubMergerIR.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1: VLogger.error(f"可达模块 .obj 缺失: {obj_path_pc}", "PhaseC")
stdlib.free(obj_path_pc)
final_obj_paths[fop_pos] = '\0'
@@ -1738,16 +1603,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
VLogger.success(f"输出: {exe_path}", "PhaseC")
# --traceback: export <exe>.stackmap (skipped under --phase2command,
# where nothing was actually linked).
if BuildPipeline._traceback_mode != 0 and BuildPipeline._phase2_cmd_path is None:
BuildPipeline.WriteStackMap(mb, exe_path)
if BuildPipeline._traceback_mode != 0 and BuildPipeline._phase2_cmd_path is None: BuildPipeline.WriteStackMap(mb, exe_path)
if args.get_bool("run") and BuildPipeline._phase2_cmd_path is not None:
# phase2command 模式:exe 尚未链接(命令在 bat 中),跳过 --run
VLogger.info("phase2command 模式:跳过 --run(exe 尚未链接)", "phase2cmd")
elif args.get_bool("run"):
VLogger.info(f"执行: {exe_path}", "run")
rp: rawptr[subprocess.CompletedProcess] = subprocess.run(exe_path, False, False)
if rp is not None:
VLogger.info(f"退出码: {rp.returncode}", "run")
if rp is not None: VLogger.info(f"退出码: {rp.returncode}", "run")
else:
VLogger.error("链接失败", "PhaseC")
stdlib.free(entries)