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
+51 -173
+51 -173
View File
@@ -17,6 +17,7 @@ import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesTranslator as HandlesTranslator import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.BuildPipeline as BuildPipeline import lib.core.BuildPipeline as BuildPipeline
import lib.StubGen.VpiReader as VpiReader import lib.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config 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) sha1_len: t.CSizeT = string.strlen(sha1)
ext_len: t.CSizeT = string.strlen(ext) ext_len: t.CSizeT = string.strlen(ext)
# 子目录前缀缓冲区(最多 3 层 = 8 字节 + null = 16 足够) # 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free)
SUBDIR_BUF_SIZE: t.CSizeT = 16 subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
subdir_buf: bytes = stdlib.malloc(SUBDIR_BUF_SIZE)
if subdir_buf is None:
return None
subdir_buf[0] = '\0'
sub_len: t.CSizeT = 0 sub_len: t.CSizeT = 0
level: int = Config.Sha1SliceLevel if subdir_buf is not None:
if level > 0: sub_len = string.strlen(subdir_buf)
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'
# 确保子目录存在: {temp_dir}/{subdir} # 确保子目录存在: {temp_dir}/{subdir}
# 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True), # 不创建目录会导致 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: if path is not None:
viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext) viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
stdlib.free(subdir_buf)
return path return path
@@ -222,10 +207,8 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
is_dot = 1 is_dot = 1
if is_dot == 0: if is_dot == 0:
# 构建子目录路径并递归扫描 # 构建子目录路径并递归扫描
fname_len: t.CSizeT = string.strlen(fname) sub_dir: str = Utils.JoinPath(base_dir, fname)
sub_dir: str = stdlib.malloc(bd_len + fname_len + 2)
if sub_dir is not None: 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) 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) out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count)
stdlib.free(sub_dir) stdlib.free(sub_dir)
@@ -395,7 +378,7 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in
if full_path_buf is None: if full_path_buf is None:
continue continue
viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath) 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) stdlib.free(full_path_buf)
if mod_name is not None: if mod_name is not None:
idx: t.CSizeT = t.CSizeT(_sha1_store_count) * 17 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: if full_path_buf is None:
return 1 return 1
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path) 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) stdlib.free(full_path_buf)
if mod_name is None: if mod_name is None:
return 1 return 1
@@ -513,34 +496,8 @@ def GetSha1StoreCount() -> int:
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到 # SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
# ============================================================ # ============================================================
def LookupSha1ByModName(mod_name: str) -> str: def LookupSha1ByModName(mod_name: str) -> str:
"""从模块名查找 SHA1(精确匹配 + 包名回退)""" """从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName"""
if mod_name is None: return _FindSha1ByModName(_sha1_store_arr, _sha1_store_mod, _sha1_store_count, mod_name)
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
# ============================================================ # ============================================================
@@ -1277,66 +1234,6 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
return out_pos 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 映射 # _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
# #
@@ -1554,66 +1451,47 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
fname: str = find_data.cFileName fname: str = find_data.cFileName
if fname is not None: if fname is not None:
fname_len: t.CSizeT = string.strlen(fname) fname_len: t.CSizeT = string.strlen(fname)
if fname_len > 3: if Utils.is_vp_or_py_at(fname, 0, fname_len) != 0:
is_py: int = 0 full_path: bytes = Utils.JoinPath(source_dir, fname)
# 检查 .vp 或 .py 扩展名(.vp 为 Viper 源,.py 向后兼容) if full_path is not None:
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'): sbuf: bytes = Utils.read_file_to_buf(full_path, SRC_BUF_SIZE_R)
is_py = 1 if sbuf is not None:
if is_py != 0: # 解析 AST 获取 _imported_modules
full_path: bytes = stdlib.malloc(dir_len + fname_len + 2) lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if full_path is not None: if lx is not None:
viperlib.snprintf(full_path, dir_len + fname_len + 2, "%s/%s", source_dir, fname) ast._lexer_init(lx, sbuf, mb)
sf: fileio.File | t.CPtr = fileio.File(full_path, fileio.MODE.R) tokens: ast.Token | t.CPtr = ast.tokenize(lx)
if not sf.closed: tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
sbuf: bytes = stdlib.malloc(SRC_BUF_SIZE_R) if tree is not None:
if sbuf is not None: tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
br: LONG = sf.read_all(sbuf, SRC_BUF_SIZE_R) if tr is not None:
sf.close() tr._declare_only = 2
if br > 0: # 源文件包名:fname 是顶级文件名(无目录分隔符),包为 None
if br < SRC_BUF_SIZE_R: tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname)
sbuf[br] = 0 HandlesStruct.reset_visible_structs(mb, 0)
else: tr.translate(tree)
sbuf[SRC_BUF_SIZE_R - 1] = 0 # 获取 _imported_modules
# 解析 AST 获取 _imported_modules if tr._imported_modules is not None:
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) im_len: t.CSizeT = string.strlen(tr._imported_modules)
if lx is not None: if im_len > 0 and wl_len + im_len + 1 < 8192:
ast._lexer_init(lx, sbuf, mb) string.strcpy(worklist + wl_len, tr._imported_modules)
tokens: ast.Token | t.CPtr = ast.tokenize(lx) wl_len += im_len
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) worklist[wl_len] = ' '
if tree is not None: wl_len += 1
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() worklist[wl_len] = '\0'
if tr is not None: # 计算 App 源文件 SHA1,填充依赖到内存存储器
tr._declare_only = 2 # worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖
# 源文件包名:fname 是顶级文件名(无目录分隔符),包为 None app_sha1_r: str = IncludesScanner.compute_file_sha1(mb, full_path)
tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname) if app_sha1_r is not None:
HandlesStruct.reset_visible_structs(mb, 0) PopulateIncludesDeps(app_sha1_r, tr._imported_modules)
tr.translate(tree) stdlib.free(app_sha1_r)
# 获取 _imported_modules # 释放 Translator 资源
if tr._imported_modules is not None: if tr._global_names is not None:
im_len: t.CSizeT = string.strlen(tr._imported_modules) stdlib.free(tr._global_names)
if im_len > 0 and wl_len + im_len + 1 < 8192: if tr._nonlocal_names is not None:
string.strcpy(worklist + wl_len, tr._imported_modules) stdlib.free(tr._nonlocal_names)
wl_len += im_len stdlib.free(sbuf)
worklist[wl_len] = ' ' stdlib.free(full_path)
wl_len += 1
worklist[wl_len] = '\0'
# 计算 App 源文件 SHA1,填充依赖到内存存储器
# worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖
app_sha1_r: str = IncludesScanner.compute_file_sha1(mb, full_path)
if app_sha1_r is not None:
PopulateIncludesDeps(app_sha1_r, tr._imported_modules)
stdlib.free(app_sha1_r)
# 释放 Translator 资源
if tr._global_names is not None:
stdlib.free(tr._global_names)
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: if win32file.FindNextFileA(handle, find_data) == 0:
break break