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:
1 parent
f2bfe82834
commit
00606057c4
2 files changed
+408
-33
No files matched your search
@@ -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
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user