fix(gen): 模块别名解析优先别名还原,修复 process 后缀误命中内核模块

import vpsdk.process as process 时,_imported_modules 含别名 "process",
store 同时存在 "vpsdk.process" 与 "VKernel.Kernel.sched.process"(ViperOS
includes 目录扫描含 VKernel)。裸名 "process" 后缀匹配命中先扫描的内核
模块 → 跨模块调用解析到未编译的内核 → 返回类型解析落空 → Phase A
_deps_pending 永不收敛(Scene3D "Phase A 无法完成翻译" 根因)。

- StructGenMixin._resolve_module_sha1:别名还原(LookupModNameByAlias →
  vpsdk.process → 精确 SHA1)提升到裸名后缀匹配之前;裸名后缀仅作兜底
- Phase2 依赖探测:同构修复,别名还原先于后缀匹配,避免依赖缺失诊断
  误报内核 process 缺 text.ll
This commit is contained in:
TermiNexus committed 2026-08-26 16:25:37 +08:00
1 parent f2bfe82834
commit 00606057c4
2 files changed
+408 -33

No files matched your search

+224 -10
View File
@@ -135,8 +135,8 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy | t.CPtr,
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
continue
# 检查是否是 .vp 或 .py 文件(.vp 为 Viper 源,.py 向后兼容)
if fname_len > 3 and Utils.is_vp_or_py_at(fname, 0, fname_len) != 0 \
# 检查是否是 .vp 源文件(Viper 源仅 .vp,不再识别 .py)
if fname_len > 3 and Utils.is_vp_at(fname, 0, fname_len) != 0 \
and file_count < max_files:
full_path: bytes = Utils.JoinPath(dir_path, fname)
if full_path is not None:
@@ -173,6 +173,108 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy | t.CPtr,
return file_count
# ============================================================
# _ScanDirForAsmFiles - 递归扫描目录,收集 .s 汇编文件
#
# 与 _ScanDirForPyFiles 同构:VKernel 等裸机项目含 .s(如
# Kernel/intr/isr.s 定义 isr0/syscall_entry/_per_cpu 等符号)。
# TPV 不对 .s 翻译,直接用 llvm-mc 汇编为 .obj 参与链接。
# ============================================================
def _ScanDirForAsmFiles(mb: memhub.MemBuddy | t.CPtr,
dir_path: str,
entries: SrcFileEntry | t.CPtr,
entry_size: t.CSizeT,
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
dir_len: t.CSizeT = string.strlen(dir_path)
pattern: bytes = stdlib.malloc(dir_len + 16)
if pattern is None:
return file_count
viperlib.snprintf(pattern, dir_len + 16, "%s/*", dir_path)
find_data_size: t.CSizeT = w32.win32file.WIN32_FIND_DATAA.__sizeof__()
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(find_data_size + 16)
if find_data is None:
stdlib.free(pattern)
return file_count
string.memset(find_data, 0, find_data_size + 16)
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
if handle == w32.win32base.INVALID_HANDLE_VALUE:
stdlib.free(pattern)
stdlib.free(find_data)
return file_count
while True:
fname: str = find_data.cFileName
if fname is None:
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
continue
fname_len: t.CSizeT = string.strlen(fname)
if fname_len <= 0:
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 is_dot != 0:
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 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
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 is_asm != 0 and file_count < max_files:
full_path: bytes = Utils.JoinPath(dir_path, fname)
if full_path is not None:
# SHA1 与 .vp 同口径(CRLF→LF 归一化),用于切片路径
sha1_val: str = IncludesScanner.compute_file_sha1(mb, full_path)
if sha1_val is not None:
ea: t.CUInt64T = t.CUInt64T(entries) + file_count * entry_size
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
if ent is not None:
ent.Path = full_path
ent.Sha1 = sha1_val
file_count += 1
fb: str = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "%s (asm sha1=%s)", fname, sha1_val)
VLogger.info(fb, "project")
# full_path 不释放:ent.Path 引用它
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
w32.win32file.FindClose(handle)
stdlib.free(pattern)
stdlib.free(find_data)
return file_count
# ============================================================
# _PreRegisterAppGenericTemplates - mark2 前预注册 App 源文件泛型类模板
#
@@ -291,8 +393,22 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
viperlib.snprintf(fb, 1024, "共 %d 个源文件", file_count)
VLogger.info(fb, "project")
# 扫描 .s 汇编文件(独立数组,不参与 Phase A 翻译,Phase B 后 llvm-mc 汇编)
asm_entries: SrcFileEntry | t.CPtr = stdlib.malloc(MAX_SRC_FILES * entry_size)
if asm_entries is None:
stdlib.free(entries)
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:
fb_asm: str = VLogger.fmt_buf()
if fb_asm is not None:
viperlib.snprintf(fb_asm, 1024, "汇编文件 %d 个(llvm-mc 汇编参与链接)", asm_file_count)
VLogger.info(fb_asm, "project")
if file_count == 0:
stdlib.free(entries)
stdlib.free(asm_entries)
return 1
# 初始化 AST 表
@@ -655,6 +771,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
continue
ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
# 计时点 0:本文件处理开始(translate 段起点)
t_pa0: t.CUInt64T = w32.win32base.GetTickCount64()
if ent is None or ent.Path is None:
done_flags[i] = 1
completed_this_pass += 1
@@ -666,6 +784,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
rel_path_pa: str = ent.Path + src_dir_len_pa + 1
pkg_pa = GenApi.compute_package_from_relpath(mb, rel_path_pa)
tr_a: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent.Path, ent.Sha1, pkg_pa, 0, temp_dir)
# 计时点 1:translate 返回
t_pa1: t.CUInt64T = w32.win32base.GetTickCount64()
if tr_a is None:
# 翻译失败(语法/解析错误):记录待下遍重试,收敛检查统一报错
# OOM 清理:失败翻译也已产生 AST/tokens 瞬时数据,立即回收
@@ -735,14 +855,26 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
continue
string.strncpy(im_dp_buf, tr_a._imported_modules + im_dp_start, im_dp_mod_len)
im_dp_buf[im_dp_mod_len] = '\0'
# 别名/基名解析:精确匹配 → 后缀匹配(import X as Y 且 Y==X 基名时命中,
# 如 GenTypes → lib.core.Gen.GenTypes;改名别名如 HT→HandlesTranslator 不命中)
# 别名/基名解析:精确匹配 → 别名还原(from_imports
# "别名:模块真名",import X as Y 的 Y)→ 后缀匹配兜底。
# 别名还原必须优先于裸名后缀:store 可能同时存在
# "vpsdk.process" 与 "VKernel.Kernel.sched.process",
# 裸名 "process" 后缀匹配会命中先扫描的内核模块 →
# 依赖探测误报内核缺 text.ll(Phase A 永不收敛)。
im_dp_sha1: str = StubMergerStore.LookupSha1ByModName(im_dp_buf)
if im_dp_sha1 is None:
im_dp_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(im_dp_buf)
# 改名别名(import X as Y):从 _from_imports 查 Y→X 还原真名,
# 再精确/后缀查 SHA1。LookupSha1ByAlias 只精确匹配——store 键
# 是全路径模块名(Kernel.drivers.fs.fat32.fat32_diskio)时
# fat32_diskio 精确不中 → 别名依赖误判 miss → 依赖未就绪死锁
# (fat32_types 依赖 diskio → Phase A 无法收敛,VKernel 根因)。
real_dp: str = StubMergerStore.LookupModNameByAlias(tr_a._from_imports, im_dp_buf)
if real_dp is not None:
im_dp_sha1 = StubMergerStore.LookupSha1ByModName(real_dp)
if im_dp_sha1 is None:
im_dp_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(real_dp)
if im_dp_sha1 is None:
# 改名别名(import X as Y):从 _from_imports 查 Y→X 再查 SHA1
im_dp_sha1 = StubMergerStore.LookupSha1ByAlias(tr_a._from_imports, im_dp_buf)
im_dp_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(im_dp_buf)
im_dp_resolved: int = 0
im_dp_missing: int = 0
if im_dp_sha1 is not None:
@@ -848,6 +980,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
df_a.close()
stdlib.free(deps_path_a)
# 计时点 2:stub+text dump 及写盘完成;输出逐文件进度(对齐 Phase1 1b)
t_pa2: t.CUInt64T = w32.win32base.GetTickCount64()
fb_pa: str = VLogger.fmt_buf()
if fb_pa is not None:
viperlib.snprintf(fb_pa, 1024,
"[%d/%d] PhaseA: %s (translate=%llu ms, dump=%llu ms, total=%llu ms)",
i + 1, file_count, ent.Path,
t_pa1 - t_pa0, t_pa2 - t_pa1, t_pa2 - t_pa0)
VLogger.info(fb_pa, "PhaseA")
done_flags[i] = 1
completed_this_pass += 1
# OOM 清理:stub/text/deps 已写盘,回收本文件翻译瞬时数据
@@ -986,12 +1128,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
i + 1, file_count, ent.Path,
t_pb1 - t_pb0, t_pb2 - t_pb1, t_pb3 - t_pb2, t_pb3 - t_pb0)
VLogger.info(fb_comp, "PhaseB")
# 构造 .obj 路径,检测是否是 main 模块(test_main.vp/.py 或 main.vp/.py)
# 构造 .obj 路径,检测是否是 main 模块(test_main.vp 或 main.vp,不再识别 .py)
od_len: t.CSizeT = string.strlen(output_dir)
is_main_mod: int = 0
if string.strstr(ent.Path, "test_main.vp") is not None or string.strstr(ent.Path, "test_main.py") is not None:
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 or string.strstr(ent.Path, "main.py") is not None:
elif string.strstr(ent.Path, "main.vp") is not None:
is_main_mod = 1
# 切片路径: output_dir/{sha1前缀}/{sha1}.obj
@@ -1033,6 +1175,40 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
ti += 1
stdlib.free(llc_tasks)
# 汇编 .s 文件:llvm-mc → .obj(isr.s 等,符号为裸名,与 .vp 的
# CExport/State stub 声明匹配)。obj 加入 obj_paths 参与 Phase C 链接。
od_len_as: t.CSizeT = string.strlen(output_dir)
asm_compiled: int = 0
for ai_as in range(asm_file_count):
ea_as: t.CUInt64T = t.CUInt64T(asm_entries) + ai_as * entry_size
ent_as: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_as, t.CPtr))
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:
fb_asm_fail: str = VLogger.fmt_buf()
if fb_asm_fail is not None:
viperlib.snprintf(fb_asm_fail, 1024, "llvm-mc 汇编失败,终止编译: %s", ent_as.Path)
VLogger.error(fb_asm_fail, "PhaseB")
sys.exit(1)
obj_path_asm: str = StubMergerCollect._sliced_path(output_dir, od_len_as, ent_as.Sha1, "obj")
if obj_path_asm is not None:
op_len_asm: t.CSizeT = string.strlen(obj_path_asm)
if obj_pos + op_len_asm + 2 < OBJ_PATHS_SIZE:
if obj_pos > 0:
obj_paths[obj_pos] = ' '
obj_pos += 1
string.strcpy(obj_paths + obj_pos, obj_path_asm)
obj_pos += op_len_asm
obj_paths[obj_pos] = '\0'
stdlib.free(obj_path_asm)
asm_compiled += 1
fb_asm_ok: str = VLogger.fmt_buf()
if fb_asm_ok is not None:
viperlib.snprintf(fb_asm_ok, 1024, "[asm %d] 汇编完成: %s", asm_compiled, ent_as.Path)
VLogger.info(fb_asm_ok, "PhaseB")
fb: str = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "编译完成: %d/%d", compiled_count, file_count)
@@ -1041,6 +1217,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
if compiled_count == 0:
VLogger.error("无成功编译的文件", "PhaseB")
stdlib.free(entries)
stdlib.free(asm_entries)
return 1
# === 3.5 编译缺失的 includes 文件(按依赖图按需编译)===
@@ -1104,6 +1281,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
temp_dir, td_len_pb, store_sha1_arr_mi, store_mod_arr_mi)
store_rel_arr_mi: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
store_incdir_arr_mi: bytes = StubMergerStore.GetSha1StoreIncdirArrPtr()
# 诊断:_BuildIncludesSha1Map 复制的条目数(PhaseB+ 循环执行前提)
fb_diag_scm: str = VLogger.fmt_buf()
if fb_diag_scm is not None:
viperlib.snprintf(fb_diag_scm, 1024, "诊断 store_count_mi=%d", store_count_mi)
VLogger.debug(fb_diag_scm, "PhaseB+")
if store_count_mi > 0:
# 拓扑排序可达 includes:被依赖模块先完整翻译。跨模块 vtable 生成
@@ -1118,6 +1300,23 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
store_count_mi, store_sha1_arr_mi, store_mod_arr_mi,
reachable_set, reachable_count, topo_idx)
# 诊断:拓扑排序条数(0 则循环不执行)
fb_diag_tc: str = VLogger.fmt_buf()
if fb_diag_tc is not None:
viperlib.snprintf(fb_diag_tc, 1024, "诊断 topo_count=%d reachable_count=%d",
topo_count, reachable_count)
VLogger.debug(fb_diag_tc, "PhaseB+")
# 诊断:dump reachable_set 内容(验证 BFS 写入的 sha1 是否有效)
ri_dbg: int = 0
for ri_dbg in range(reachable_count):
rsha1_dbg: str = reachable_set + t.CSizeT(ri_dbg) * 17
fb_diag_rs: str = VLogger.fmt_buf()
if fb_diag_rs is not None:
viperlib.snprintf(fb_diag_rs, 1024, "诊断 reachable[%d]=%s",
ri_dbg, rsha1_dbg)
VLogger.debug(fb_diag_rs, "PhaseB+")
for si_mi in range(topo_count):
# 当前条目(拓扑序索引,仅含可达模块)
ti: int = topo_idx[si_mi]
@@ -1141,6 +1340,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
stdlib.free(check_fd_mi)
continue
# 诊断:obj 不存在,进入 is_decl 判定
fb_diag_obj: str = VLogger.fmt_buf()
if fb_diag_obj is not None:
viperlib.snprintf(fb_diag_obj, 1024, "诊断 循环 si=%d ti=%d sha1=%s obj_exists=%d",
si_mi, ti, inc_sha1_mi, obj_exists_mi)
VLogger.debug(fb_diag_obj, "PhaseB+")
# .obj 不存在,需要编译
# 构造源文件路径: {所属 includes 根目录}/{rel_path}(多目录逐条取 IncDir,空串回退 includes_dir)
rel_path_len_mi: t.CSizeT = string.strlen(inc_rel_mi)
@@ -1193,6 +1399,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
stdlib.free(tbuf_mi)
tf_mi.close()
stdlib.free(tpath_mi)
# 诊断:is_decl 判定结果
fb_diag_decl: str = VLogger.fmt_buf()
if fb_diag_decl is not None:
viperlib.snprintf(fb_diag_decl, 1024, "诊断 is_decl=%d src=%s",
is_decl_mi, src_fp_mi)
VLogger.debug(fb_diag_decl, "PhaseB+")
# is_decl_mi == -1: text.ll 不存在(Phase1 未翻译此文件)
# is_decl_mi == 1: 声明文件(仅 declare 无 define)
# is_decl_mi == 0: 实现文件(有 define)
@@ -1475,7 +1687,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
else:
VLogger.error("链接失败", "PhaseC")
stdlib.free(entries)
stdlib.free(asm_entries)
return 1
stdlib.free(entries)
stdlib.free(asm_entries)
return 0
+184 -23
View File
@@ -12,6 +12,7 @@ import hashtable
import viperlib
import lib.core.CompilerFactory as CompilerFactory
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
# ============================================================
@@ -354,7 +355,7 @@ class StructGenMixin:
字段默认值(fe.DefaultVal)除定义文件内 _generate_before_init 使用外,
跨文件结构体构造(_translate_struct_ctor → _apply_struct_defaults)会在
调用方文件翻译时读取——原 pool AST 已 reset 回收必须深拷贝。
支持 Constant(标量)与 List(列表字面量,如 t.CArray[str] = ["py", "pyw"])。
支持 Constant(标量)与 List(列表字面量,如 t.CArray[str] = [...])。
其他非常量默认值(Name/BinOp 等引用表达式)无法安全深拷贝:返回 None
(调用方 fe.DefaultVal 保持 None,仅定义文件内 _generate_before_init
仍持有原始 pool 指针,不受影响)。
@@ -448,11 +449,43 @@ class StructGenMixin:
"""返回当前已注册的结构体数量"""
return self.StructCount
def _resolve_module_sha1(self, mod_name: str,
from_imports: str = None) -> str:
"""模块名 → 模块 SHA1(精确 → import 别名还原后再查 → 后缀兜底)
模块名可能是:模块真名(fat32_types)、包后缀(sched.sched)、
import 别名(import X as Y 的 Y)。逐级尝试:
1. LookupSha1ByModName:精确 + "__init__" 包回退
2. LookupModNameByAlias:from_imports "别名:模块真名" 还原后
精确/后缀重试——必须优先于裸名后缀匹配:store 可能同时存在
"vpsdk.process" 与 "VKernel.Kernel.sched.process",裸名
"process" 后缀匹配会命中先扫描的内核模块(别名 process 被
劫持 → 跨模块调用解析到未编译的内核 → Phase A _deps_pending
永不收敛,Scene3D root cause)
3. LookupSha1ByModNameSuffix:后缀匹配(store 键是全路径模块名,
如 "Kernel.drivers.fs.fat32.fat32_types"),非别名裸名兜底
"""
if mod_name is None:
return None
s_rm: str = StubMergerStore.LookupSha1ByModName(mod_name)
if s_rm is None and from_imports is not None:
real_rm: str = StubMergerStore.LookupModNameByAlias(from_imports, mod_name)
if real_rm is not None:
s_rm = StubMergerStore.LookupSha1ByModName(real_rm)
if s_rm is None:
s_rm = StubMergerStore.LookupSha1ByModNameSuffix(real_rm)
if s_rm is None:
s_rm = StubMergerStore.LookupSha1ByModNameSuffix(mod_name)
return s_rm
def find_struct(self, name: str) -> GenTypes.StructEntry | t.CPtr:
"""按类名查找结构体,返回 StructEntry 或 None
支持模块限定名(如 "HT.Translator"):
第一遍精确匹配,第二遍按类名(最后一个 '.' 之后的部分)回退
第一遍精确匹配,第二遍按类名(最后一个 '.' 之后的部分)回退。
模块可解析的限定名(module.Class)按模块归属精确匹配,不做裸名
跨库回退(防 sched.Thread 被 vthreading.Thread 顶掉的同类问题);
仅模块名无法解析时保留裸名回退(兼容旧行为)。
"""
if name is None or self.StructTable is None:
return None
@@ -466,7 +499,42 @@ class StructGenMixin:
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
# 第二遍:类名回退(处理模块限定名 "HT.Translator" → "Translator")
# 模块限定名(module.Class):模块可解析时按模块归属精确匹配,
# 防止裸名回退抓跨模块同名类。模块未命中时若裸名类唯一(无冲突)
# 仍回退,避免误杀未登记模块的既有解析。
dot_pos_q: str = string.strrchr(name, '.')
if dot_pos_q is not None and dot_pos_q[1] != '\0':
bare_f: str = dot_pos_q + 1
mod_len_f: t.CSizeT = string.strlen(name) - string.strlen(bare_f) - 1
if mod_len_f > 0:
mod_buf_f: str = stdlib.malloc(mod_len_f + 1)
if mod_buf_f is not None:
string.strncpy(mod_buf_f, name, mod_len_f)
mod_buf_f[mod_len_f] = '\0'
if string.strcmp(mod_buf_f, "t") != 0 and string.strcmp(mod_buf_f, "c") != 0:
mod_sha1_f: str = self._resolve_module_sha1(mod_buf_f)
if mod_sha1_f is not None:
me_f: GenTypes.StructEntry | t.CPtr = \
self.find_struct_by_module(bare_f, mod_sha1_f)
if me_f is not None:
stdlib.free(mod_buf_f)
return me_f
# 模块类未注册:统计裸名同名类数量,唯一时回退(无冲突)
same_cnt_f: int = 0
same_entry_f: GenTypes.StructEntry | t.CPtr = None
for i2 in range(self.StructCount):
e2: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i2)
if e2 is not None and e2.Name is not None:
if string.strcmp(e2.Name, bare_f) == 0:
same_cnt_f += 1
same_entry_f = e2
stdlib.free(mod_buf_f)
if same_cnt_f == 1:
return same_entry_f
return None
stdlib.free(mod_buf_f)
# 第二遍:类名回退(处理模块限定名 "HT.Translator" → "Translator",
# 仅模块名无法解析时走到这里)
dot_pos: str = string.strrchr(name, '.')
if dot_pos is not None:
class_name: str = dot_pos + 1
@@ -627,10 +695,16 @@ class StructGenMixin:
def lookup_field_by_class(self, class_name: str,
field_name: str,
sha1: str = None) -> GenTypes.FieldEntry | t.CPtr:
sha1: str = None,
node: ast.AST | t.CPtr = None,
from_imports: str = None) -> GenTypes.FieldEntry | t.CPtr:
"""按类名和字段名查找字段信息
优先用 SHA1 匹配(跨模块同名类区分),无 SHA1 时按类名查找第一个
优先用 SHA1 匹配(跨模块同名类区分),无 SHA1 时按类名查找第一个。
node: 出错位置 AST 节点(可选,供失败报错输出 Python 风格 traceback 帧;
传 None 时用 trace 栈兜底定位)
from_imports: from-import 映射("local:module" 格式),供模块限定名
的 import 别名(import X as Y → Y.Class)解析为模块 sha1
"""
if class_name is None or field_name is None:
return None
@@ -668,16 +742,76 @@ class StructGenMixin:
return fe
return None
# 模块限定名(module.Class,module 非 t/c 内置):按模块归属解析,
# 禁止裸名回退抓跨模块同名类(根因:VKernel sched.Thread 被 includes/
# vthreading.vp 同名 Thread 顶掉 → 'tid' 字段缺失)。
# 模块名经 StubMergerStore sha1 映射表解析为模块 sha1,再精确匹配
# name+sha1(vpi stub 注入 / 定义侧注册的裸名 entry)。
dot_pos_q: str = string.strrchr(class_name, '.')
if dot_pos_q is not None and dot_pos_q[1] != '\0':
bare_q: str = dot_pos_q + 1
mod_len_q: t.CSizeT = string.strlen(class_name) - string.strlen(bare_q) - 1
if mod_len_q > 0:
mod_buf_q: str = stdlib.malloc(mod_len_q + 1)
if mod_buf_q is not None:
string.strncpy(mod_buf_q, class_name, mod_len_q)
mod_buf_q[mod_len_q] = '\0'
if string.strcmp(mod_buf_q, "t") != 0 and string.strcmp(mod_buf_q, "c") != 0:
mod_sha1_q: str = self._resolve_module_sha1(mod_buf_q, from_imports)
if mod_sha1_q is not None:
mod_entry_q: GenTypes.StructEntry | t.CPtr = \
self.find_struct_by_module(bare_q, mod_sha1_q)
if mod_entry_q is not None:
for fi_q in range(mod_entry_q.FieldCount):
fe_q: GenTypes.FieldEntry | t.CPtr = \
self._get_field_entry(mod_entry_q, fi_q)
if fe_q is not None and fe_q.Name is not None:
if string.strcmp(fe_q.Name, field_name) == 0:
stdlib.free(mod_buf_q)
return fe_q
# 类命中但字段未找到:可读化报错(traceback 格式)
fb_qf: str = VLogger.fmt_buf()
if fb_qf is not None:
viperlib.snprintf(fb_qf, 1024,
"lookup_field_by_class: 类 '%s' 已按模块归属解析"
"(ModuleSha1=%s, FieldCount=%d)但字段 '%s' 未找到",
class_name, mod_sha1_q, mod_entry_q.FieldCount, field_name)
self.Gen.TypeGenErr.fatal_error(node, fb_qf, "AttributeError")
stdlib.free(mod_buf_q)
return None
# 模块可解析但类未注册:fail-fast(暴露 vpi 注入/注册缺陷,
# 不再裸名跨库抓其他模块同名类)
fb_qm: str = VLogger.fmt_buf()
if fb_qm is not None:
viperlib.snprintf(fb_qm, 1024,
"lookup_field_by_class: 类 '%s' 未找到(模块 '%s' 可解析"
" sha1=%s,但无同名注册类;请检查 vpi stub 注入或编译顺序)",
class_name, mod_buf_q, mod_sha1_q)
self.Gen.TypeGenErr.fatal_error(node, fb_qm, "AttributeError")
stdlib.free(mod_buf_q)
return None
# 模块名未登记:fail-fast(不再裸名跨库回退)
fb_qs: str = VLogger.fmt_buf()
if fb_qs is not None:
viperlib.snprintf(fb_qs, 1024,
"lookup_field_by_class: 类 '%s' 的模块 '%s' 无法解析"
"(sha1 映射表未登记,请检查 import 与编译顺序)",
class_name, mod_buf_q)
self.Gen.TypeGenErr.fatal_error(node, fb_qs, "AttributeError")
stdlib.free(mod_buf_q)
return None
stdlib.free(mod_buf_q)
# 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个
entry = self.find_struct(class_name)
if entry is None:
# 诊断:目标类未注册
# 诊断:目标类未注册(仿 Python traceback 格式,立即终止编译)
fb_lfbc: str = VLogger.fmt_buf()
if fb_lfbc is not None:
viperlib.snprintf(fb_lfbc, 1024,
"lookup_field_by_class: 类 '%s' 未注册 (struct_count=%d)",
class_name, self.StructCount)
VLogger.error(fb_lfbc, "STRUCT")
self.Gen.TypeGenErr.fatal_error(node, fb_lfbc, "AttributeError")
return None
# 先查找字段(诊断/error 之前返回,否则 error 立即退出导致查找永远失败)
for fi in range(entry.FieldCount):
@@ -715,18 +849,19 @@ class StructGenMixin:
self.Gen.StructGen._get_field_entry(entry, rn_alias_l)
if rn_fe_l is not None and rn_fe_l.Ty is not None:
return rn_fe_l
# 诊断:找到结构体但字段确实未找到,打印字段表
# 注意:VLogger.error 会立即退出进程,字段表须在 error 之前用 warning 打印
for fi in range(entry.FieldCount):
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
if fe is not None:
fb_fn: str = VLogger.fmt_buf()
if fb_fn is not None:
if fe.Name is not None:
viperlib.snprintf(fb_fn, 1024, " 字段[%d]: '%s'", fi, fe.Name)
else:
viperlib.snprintf(fb_fn, 1024, " 字段[%d]: (null)", fi)
VLogger.warning(fb_fn, "STRUCT")
# 诊断:找到结构体但字段确实未找到
# 拼接注册字段列表(一行逗号分隔,供报错一眼定位)
# 注意:不能用 VLogger.fmt_buf()(与 fb_fields 同一静态缓冲,会被覆盖)
fb_flds_lst: str = stdlib.malloc(1024)
if fb_flds_lst is not None:
fb_flds_lst[0] = '\0'
for fi in range(entry.FieldCount):
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
if fe is not None and fe.Name is not None:
if string.strlen(fb_flds_lst) > 0 and string.strlen(fb_flds_lst) + 3 < 1024:
string.strcat(fb_flds_lst, ", ")
if string.strlen(fb_flds_lst) + string.strlen(fe.Name) < 1024:
string.strcat(fb_flds_lst, fe.Name)
fb_fields: str = VLogger.fmt_buf()
if fb_fields is not None:
if rn_is_renum_l != 0:
@@ -739,10 +874,30 @@ class StructGenMixin:
"请检查该变体字段是否已注册别名,或 class_name 指针是否统一)",
class_name, entry.FieldCount, field_name, rn_cls_l, rn_alias_l)
else:
# 可读化:附 entry.Name/ModuleSha1(跨模块同名定位)+ 注册字段
# 列表 + 期望 sha1(调用方)→ 一眼分辨"跨模块同名类误匹配"
# vs "vpi stub 注入字段不全" vs "类定义与访问处不一致"
sha1_txt: str = "?"
if entry.ModuleSha1 is not None:
sha1_txt = entry.ModuleSha1
want_sha1_txt: str = "?"
if sha1 is not None:
want_sha1_txt = sha1
fld_lst_txt: str = "(无)"
if fb_flds_lst is not None and string.strlen(fb_flds_lst) > 0:
fld_lst_txt = fb_flds_lst
viperlib.snprintf(fb_fields, 1024,
"lookup_field_by_class: 类 '%s' 已注册 (FieldCount=%d) 但字段 '%s' 未找到",
class_name, entry.FieldCount, field_name)
VLogger.error(fb_fields, "STRUCT")
"lookup_field_by_class: 类 '%s' 已注册 (FieldCount=%d, "
"entry.Name='%s', ModuleSha1=%s, 调用方期望 sha1=%s) "
"但字段 '%s' 未找到;注册字段: [%s]。可能原因: 跨模块同名类"
"被误匹配 / vpi stub 注入字段不全 / 类定义与访问处不一致",
class_name, entry.FieldCount, entry.Name, sha1_txt,
want_sha1_txt, field_name, fld_lst_txt)
# 仿 Python traceback 格式输出(Traceback 帧 + 文件:行号 + 错误类型),
# 立即终止编译(与 VLogger.error 同语义,格式对齐 fatal_error)
self.Gen.TypeGenErr.fatal_error(node, fb_fields, "AttributeError")
if fb_flds_lst is not None:
stdlib.free(fb_flds_lst)
# 诊断:找到结构体但字段未找到
return None
@@ -790,13 +945,19 @@ class StructGenMixin:
return None
ty: llvmlite.LLVMType | t.CPtr = val.Ty
while ty is not None:
# 同类编译 bug(见 TypeGenResolve L1463 注释):case body 直接
# 更新 while 条件变量(ty = pointee)时自举编译出的 match 把赋值
# 写到 subject 快照槽,while 条件读旧槽 → 死循环。改法:case
# 绑定到局部变量,match 外更新循环变量。
nxt_ty: llvmlite.LLVMType | t.CPtr = None
match ty:
case llvmlite.LLVMType.Ptr(pointee):
ty = pointee
nxt_ty = pointee
case llvmlite.LLVMType.Struct(fields, fcount, name):
return ty
case _:
return None
ty = nxt_ty
return None
def mark_as_union(self, name: str) -> int: