diff --git a/App/lib/core/StubMerger.vp b/App/lib/core/StubMerger.vp index 34616f8..e18af2f 100644 --- a/App/lib/core/StubMerger.vp +++ b/App/lib/core/StubMerger.vp @@ -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,66 +1451,47 @@ 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 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) - 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: - ast._lexer_init(lx, sbuf, mb) - tokens: ast.Token | t.CPtr = ast.tokenize(lx) - tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) - if tree is not None: - tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() - if tr is not None: - tr._declare_only = 2 - # 源文件包名:fname 是顶级文件名(无目录分隔符),包为 None - tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname) - HandlesStruct.reset_visible_structs(mb, 0) - tr.translate(tree) - # 获取 _imported_modules - if tr._imported_modules is not None: - im_len: t.CSizeT = string.strlen(tr._imported_modules) - if im_len > 0 and wl_len + im_len + 1 < 8192: - string.strcpy(worklist + wl_len, tr._imported_modules) - wl_len += im_len - worklist[wl_len] = ' ' - 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 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: + sbuf: bytes = Utils.read_file_to_buf(full_path, SRC_BUF_SIZE_R) + if sbuf is not None: + # 解析 AST 获取 _imported_modules + lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) + if lx is not None: + ast._lexer_init(lx, sbuf, mb) + tokens: ast.Token | t.CPtr = ast.tokenize(lx) + tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) + if tree is not None: + tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() + if tr is not None: + tr._declare_only = 2 + # 源文件包名:fname 是顶级文件名(无目录分隔符),包为 None + tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname) + HandlesStruct.reset_visible_structs(mb, 0) + tr.translate(tree) + # 获取 _imported_modules + if tr._imported_modules is not None: + im_len: t.CSizeT = string.strlen(tr._imported_modules) + if im_len > 0 and wl_len + im_len + 1 < 8192: + string.strcpy(worklist + wl_len, tr._imported_modules) + wl_len += im_len + worklist[wl_len] = ' ' + 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) + stdlib.free(full_path) if win32file.FindNextFileA(handle, find_data) == 0: break