refactor: StubMerger 复用公共工具(LookupSha1ByModName 委托、_sliced_path 复用 slice_subdir、_PathToModuleName 下沉)

This commit is contained in:
TermiNexus committed 2026-08-08 00:02:39 +08:00
1 parent 96e045889e
commit 0665ee1164
1 file changed
+13 -135
+13 -135
View File
@@ -17,6 +17,7 @@ import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.BuildPipeline as BuildPipeline
import lib.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
@@ -94,26 +95,11 @@ def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str:
sha1_len: t.CSizeT = string.strlen(sha1)
ext_len: t.CSizeT = string.strlen(ext)
# 子目录前缀缓冲区(最多 3 层 = 8 字节 + null = 16 足够)
SUBDIR_BUF_SIZE: t.CSizeT = 16
subdir_buf: bytes = stdlib.malloc(SUBDIR_BUF_SIZE)
if subdir_buf is None:
return None
subdir_buf[0] = '\0'
# 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free)
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
sub_len: t.CSizeT = 0
level: int = Config.Sha1SliceLevel
if level > 0:
li: int = 0
while li < level:
if li > 0:
subdir_buf[sub_len] = '/'
sub_len += 1
idx: int = li * 2
subdir_buf[sub_len] = sha1[idx]
subdir_buf[sub_len + 1] = sha1[idx + 1]
sub_len += 2
li += 1
subdir_buf[sub_len] = '\0'
if subdir_buf is not None:
sub_len = string.strlen(subdir_buf)
# 确保子目录存在: {temp_dir}/{subdir}
# 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True),
@@ -139,7 +125,6 @@ def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str:
if path is not None:
viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
stdlib.free(subdir_buf)
return path
@@ -222,10 +207,8 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
is_dot = 1
if is_dot == 0:
# 构建子目录路径并递归扫描
fname_len: t.CSizeT = string.strlen(fname)
sub_dir: str = stdlib.malloc(bd_len + fname_len + 2)
sub_dir: str = Utils.JoinPath(base_dir, fname)
if sub_dir is not None:
viperlib.snprintf(sub_dir, bd_len + fname_len + 2, "%s/%s", base_dir, fname)
sub_dir_len: t.CSizeT = string.strlen(sub_dir)
out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count)
stdlib.free(sub_dir)
@@ -395,7 +378,7 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in
if full_path_buf is None:
continue
viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath)
mod_name: str = _PathToModuleName(full_path_buf)
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
stdlib.free(full_path_buf)
if mod_name is not None:
idx: t.CSizeT = t.CSizeT(_sha1_store_count) * 17
@@ -467,7 +450,7 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
if full_path_buf is None:
return 1
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
mod_name: str = _PathToModuleName(full_path_buf)
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
stdlib.free(full_path_buf)
if mod_name is None:
return 1
@@ -513,34 +496,8 @@ def GetSha1StoreCount() -> int:
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
# ============================================================
def LookupSha1ByModName(mod_name: str) -> str:
"""从模块名查找 SHA1(精确匹配 + 包名回退)"""
if mod_name is None:
return None
if _sha1_store_arr is None or _sha1_store_mod is None:
return None
if _sha1_store_count <= 0:
return None
# 第一遍:精确匹配
i_em: int
for i_em in range(_sha1_store_count):
idx_em: t.CSizeT = t.CSizeT(i_em) * 64
if string.strcmp(_sha1_store_mod + idx_em, mod_name) == 0:
sidx_em: t.CSizeT = t.CSizeT(i_em) * 17
return _sha1_store_arr + sidx_em
# 第二遍:包名回退("ast" → "ast.__init__")
nm_len_em: t.CSizeT = string.strlen(mod_name)
init_buf_em: bytes = stdlib.malloc(nm_len_em + 12)
if init_buf_em is not None:
viperlib.snprintf(init_buf_em, nm_len_em + 12, "%s.__init__", mod_name)
i_im: int
for i_im in range(_sha1_store_count):
idx2_im: t.CSizeT = t.CSizeT(i_im) * 64
if string.strcmp(_sha1_store_mod + idx2_im, init_buf_em) == 0:
sidx2_im: t.CSizeT = t.CSizeT(i_im) * 17
stdlib.free(init_buf_em)
return _sha1_store_arr + sidx2_im
stdlib.free(init_buf_em)
return None
"""从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName"""
return _FindSha1ByModName(_sha1_store_arr, _sha1_store_mod, _sha1_store_count, mod_name)
# ============================================================
@@ -1277,66 +1234,6 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
return out_pos
# ============================================================
# _PathToModuleName - 将文件路径转换为模块名
#
# 输入: "includes/stdio.vp" 或 "includes/lib/core/Handles/HandlesType.vp"
# 输出: "stdio" 或 "lib.core.Handles.HandlesType"
# 去除 includes/ 前缀和 .vp/.py 后缀,/ 替换为 .
# 返回 stdlib.malloc 分配的字符串,调用者负责释放
# ============================================================
def _PathToModuleName(path: str) -> str:
"""将文件路径转换为模块名
特殊处理 __init__.vp:includes/ast/__init__.vp → ast(而非 ast.__init__)
"""
if path is None:
return None
plen: t.CSizeT = string.strlen(path)
# 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容)
prefix_len: t.CSizeT = 9
path_start: t.CSizeT = 0
if plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
psep_ch: t.CChar = path[8]
if psep_ch == '/' or psep_ch == '\\':
path_start = prefix_len
plen = plen - prefix_len
# 去掉 .vp 或 .py 后缀(.vp 为 Viper 源,.py 向后兼容)
if plen > 3:
if (path[path_start + plen - 3] == '.' and path[path_start + plen - 2] == 'v' and path[path_start + plen - 1] == 'p') or (path[path_start + plen - 3] == '.' and path[path_start + plen - 2] == 'p' and path[path_start + plen - 1] == 'y'):
plen -= 3
if plen == 0:
return None
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.vp)
# /__init__ 是 9 个字符:/ _ _ i n i t _ _
# 如果是,去掉分隔符+__init__ 后缀,模块名就是包名(如 ast/__init__ → ast)
# 注意:顶级 __init__.vp(plen=8)不会进入此分支,因为 plen > 9 才检查
# 同时支持 / 和 \ 两种分隔符(TPC 写入的路径可能混合使用)
if plen > 9:
tail_off: t.CSizeT = path_start + plen - 9
sep_char: t.CChar = path[tail_off]
if (sep_char == '/' or sep_char == '\\') \
and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' \
and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' \
and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
# 去掉 分隔符+__init__ 后缀,plen 就是包名部分长度
plen = tail_off - path_start
if plen == 0:
return None
# 复制并替换 / 为 .
result: str = stdlib.malloc(plen + 1)
if result is None:
return None
for ri in range(plen):
ch: t.CChar = path[path_start + ri]
if ch == '/' or ch == '\\':
result[ri] = '.'
else:
result[ri] = ch
result[plen] = '\0'
return result
# ============================================================
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
#
@@ -1554,26 +1451,11 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
fname: str = find_data.cFileName
if fname is not None:
fname_len: t.CSizeT = string.strlen(fname)
if fname_len > 3:
is_py: int = 0
# 检查 .vp 或 .py 扩展名(.vp 为 Viper 源,.py 向后兼容)
if (fname[fname_len - 3] == '.' and fname[fname_len - 2] == 'v' and fname[fname_len - 1] == 'p') or (fname[fname_len - 3] == '.' and fname[fname_len - 2] == 'p' and fname[fname_len - 1] == 'y'):
is_py = 1
if is_py != 0:
full_path: bytes = stdlib.malloc(dir_len + fname_len + 2)
if Utils.is_vp_or_py_at(fname, 0, fname_len) != 0:
full_path: bytes = Utils.JoinPath(source_dir, fname)
if full_path is not None:
viperlib.snprintf(full_path, dir_len + fname_len + 2, "%s/%s", source_dir, fname)
sf: fileio.File | t.CPtr = fileio.File(full_path, fileio.MODE.R)
if not sf.closed:
sbuf: bytes = stdlib.malloc(SRC_BUF_SIZE_R)
sbuf: bytes = Utils.read_file_to_buf(full_path, SRC_BUF_SIZE_R)
if sbuf is not None:
br: LONG = sf.read_all(sbuf, SRC_BUF_SIZE_R)
sf.close()
if br > 0:
if br < SRC_BUF_SIZE_R:
sbuf[br] = 0
else:
sbuf[SRC_BUF_SIZE_R - 1] = 0
# 解析 AST 获取 _imported_modules
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if lx is not None:
@@ -1609,10 +1491,6 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
if tr._nonlocal_names is not None:
stdlib.free(tr._nonlocal_names)
stdlib.free(sbuf)
else:
stdlib.free(sbuf)
else:
sf.close()
stdlib.free(full_path)
if win32file.FindNextFileA(handle, find_data) == 0: