直接移动+改调用方模式:35 个函数按功能移至 5 个子模块,主文件 仅保留常量与全局变量(main.vp 仍直接赋值 StubMerger._mbuddy)。 - 子模块函数体内裸调用映射为子模块前缀,全局变量经 StubMerger._xxx 跨模块访问 - 12 个调用方文件 StubMerger.func( → 对应子模块前缀并补 import - 全 App 语法/引用/import 验证通过
287 lines
12 KiB
Plaintext
287 lines
12 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import memhub
|
||
import string
|
||
import stdlib
|
||
import llvmlite
|
||
import w32.win32file as win32file
|
||
import w32.win32base as win32base
|
||
import w32.fileio as fileio
|
||
import viperlib
|
||
import sys
|
||
import ast
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.IncludesScanner as IncludesScanner
|
||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||
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
|
||
|
||
|
||
import lib.core.StubMergerStore as StubMergerStore
|
||
import lib.core.StubMergerTopo as StubMergerTopo
|
||
import lib.core.StubMergerIR as StubMergerIR
|
||
import lib.core.StubMergerBuild as StubMergerBuild
|
||
import lib.core.StubMerger as StubMerger
|
||
|
||
# ============================================================
|
||
# StubMergerCollect - StubMerger 拆分(阶段 E):StubMergerCollect
|
||
#
|
||
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
|
||
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
|
||
# StubMerger._xxx 访问(main.vp 先例)。
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free)
|
||
#
|
||
# 根据 Config.Sha1SliceLevel 将文件分散到 SHA1 前缀子目录:
|
||
# level=0 → {temp_dir}/{sha1}.{ext}
|
||
# level=1 → {temp_dir}/b7/{sha1}.{ext}
|
||
# level=2 → {temp_dir}/b7/90/{sha1}.{ext}
|
||
#
|
||
# 自动调用 BuildPipeline.ensure_dir 创建子目录(level>0 时)。
|
||
#
|
||
# Args:
|
||
# temp_dir: 基础目录
|
||
# td_len: temp_dir 长度(避免重复 strlen)
|
||
# sha1: SHA1 字符串
|
||
# ext: 文件扩展名(如 "stub.ll")
|
||
#
|
||
# Returns:
|
||
# 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败
|
||
# ============================================================
|
||
def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str:
|
||
"""构建切片路径(stdlib.malloc 分配,调用者负责 free)
|
||
|
||
level>0 时自动调用 ensure_dir 创建子目录。
|
||
"""
|
||
if temp_dir is None or sha1 is None or ext is None:
|
||
return None
|
||
sha1_len: t.CSizeT = string.strlen(sha1)
|
||
ext_len: t.CSizeT = string.strlen(ext)
|
||
|
||
# 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free)
|
||
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
||
sub_len: t.CSizeT = 0
|
||
if subdir_buf is not None:
|
||
sub_len = string.strlen(subdir_buf)
|
||
|
||
# 确保子目录存在: {temp_dir}/{subdir}
|
||
# 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True),
|
||
# 进而 BuildCombinedIR 读不到文件返回 0("BuildCombinedIR 失败")。
|
||
dir_path_len: t.CSizeT = td_len + sub_len + 2
|
||
dir_path: bytes = stdlib.malloc(dir_path_len)
|
||
if dir_path is not None:
|
||
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
|
||
BuildPipeline.ensure_dir(dir_path)
|
||
stdlib.free(dir_path)
|
||
|
||
# 构建完整路径
|
||
path: str = None
|
||
path_len: t.CSizeT = 0
|
||
if sub_len > 0:
|
||
path_len = td_len + sub_len + sha1_len + ext_len + 4
|
||
path = stdlib.malloc(path_len)
|
||
if path is not None:
|
||
viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
|
||
else:
|
||
path_len = td_len + sha1_len + ext_len + 3
|
||
path = stdlib.malloc(path_len)
|
||
if path is not None:
|
||
viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
|
||
|
||
return path
|
||
# ============================================================
|
||
# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1
|
||
#
|
||
# 根据 level 递归扫描子目录:
|
||
# level=0 → 扫描 {base_dir}/*.stub.ll,从文件名提取 SHA1
|
||
# level>0 → 扫描 {base_dir}/* 的子目录,对每个子目录递归调用
|
||
#
|
||
# Args:
|
||
# base_dir: 当前扫描目录
|
||
# bd_len: base_dir 长度
|
||
# level: 剩余递归层数
|
||
# out_arr: 输出数组(每个 SHA1 17 字节 = 16字符 + null)
|
||
# out_count: 当前已收集数量
|
||
# max_count: 最大数量
|
||
#
|
||
# Returns:
|
||
# 收集后的总数量
|
||
# ============================================================
|
||
def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
|
||
out_arr: str, out_count: int, max_count: int) -> int:
|
||
"""递归扫描子目录,收集 .stub.ll 文件的 SHA1"""
|
||
if base_dir is None or out_arr is None:
|
||
return out_count
|
||
if out_count >= max_count:
|
||
return out_count
|
||
|
||
find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__())
|
||
if find_data is None:
|
||
return out_count
|
||
string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__())
|
||
|
||
if level <= 0:
|
||
# 扫描 {base_dir}/*.stub.ll
|
||
pattern: bytes = stdlib.malloc(bd_len + 16)
|
||
if pattern is None:
|
||
stdlib.free(find_data)
|
||
return out_count
|
||
viperlib.snprintf(pattern, bd_len + 16, "%s/*.stub.ll", base_dir)
|
||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
||
stdlib.free(pattern)
|
||
if handle != win32base.INVALID_HANDLE_VALUE:
|
||
while 1:
|
||
if out_count >= max_count:
|
||
break
|
||
fname: str = find_data.cFileName
|
||
if fname is not None:
|
||
if fname[0] != '\0':
|
||
# 从文件名提取 SHA1(前 16 字符)
|
||
string.strncpy(out_arr + out_count * 17, fname, 16)
|
||
out_arr[out_count * 17 + 16] = '\0'
|
||
out_count += 1
|
||
if win32file.FindNextFileA(handle, find_data) == 0:
|
||
break
|
||
win32file.FindClose(handle)
|
||
else:
|
||
# 扫描 {base_dir}/* 的子目录
|
||
pattern = stdlib.malloc(bd_len + 4)
|
||
if pattern is None:
|
||
stdlib.free(find_data)
|
||
return out_count
|
||
viperlib.snprintf(pattern, bd_len + 4, "%s/*", base_dir)
|
||
handle = win32file.FindFirstFileA(pattern, find_data)
|
||
stdlib.free(pattern)
|
||
if handle != win32base.INVALID_HANDLE_VALUE:
|
||
while 1:
|
||
# 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16)
|
||
attrs: t.CUInt32T = find_data.dwFileAttributes
|
||
if(attrs & 16) != 0:
|
||
fname = find_data.cFileName
|
||
if fname is not None:
|
||
# 跳过 "." 和 ".."
|
||
is_dot: int = 0
|
||
if fname[0] == '.':
|
||
if fname[1] == '\0':
|
||
is_dot = 1
|
||
elif fname[1] == '.' and fname[2] == '\0':
|
||
is_dot = 1
|
||
if is_dot == 0:
|
||
# 构建子目录路径并递归扫描
|
||
sub_dir: str = Utils.JoinPath(base_dir, fname)
|
||
if sub_dir is not None:
|
||
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)
|
||
if win32file.FindNextFileA(handle, find_data) == 0:
|
||
break
|
||
win32file.FindClose(handle)
|
||
|
||
stdlib.free(find_data)
|
||
return out_count
|
||
# ============================================================
|
||
# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1
|
||
#
|
||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
||
# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。
|
||
# ============================================================
|
||
def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr,
|
||
temp_dir: str,
|
||
sha1_set: str) -> int:
|
||
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
|
||
|
||
pool/temp_dir 参数保留以兼容现有调用者,但不再使用。
|
||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
||
sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null)
|
||
返回找到的 includes SHA1 数量,-1 表示错误"""
|
||
if sha1_set is None:
|
||
return -1
|
||
if StubMerger._sha1_store_arr is None:
|
||
return -1
|
||
count: int = 0
|
||
for i in range(StubMerger._sha1_store_count):
|
||
if count >= MAX_INCLUDES_SHA1:
|
||
break
|
||
idx: t.CSizeT = t.CSizeT(i) * 17
|
||
string.strcpy(sha1_set + t.CSizeT(count) * 17, StubMerger._sha1_store_arr + idx)
|
||
count += 1
|
||
return count
|
||
# ============================================================
|
||
# _is_in_sha1_set - 检查 SHA1 是否在集合中
|
||
# ============================================================
|
||
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||
"""检查 sha1 是否在集合中"""
|
||
if sha1 is None or sha1_set is None:
|
||
return 0
|
||
for i in range(set_count):
|
||
if string.strcmp(sha1_set + i * 17, sha1) == 0:
|
||
return 1
|
||
return 0
|
||
# ============================================================
|
||
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出)
|
||
#
|
||
# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。
|
||
# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器。
|
||
#
|
||
# Args:
|
||
# mb: 内存池
|
||
# temp_dir: 临时目录(_sha1_map.txt 写入位置)
|
||
# scan_result: IncludesScanner 扫描结果
|
||
#
|
||
# Returns:
|
||
# 0 成功,非 0 失败
|
||
# ============================================================
|
||
def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
|
||
scan_result: IncludesScanner.ScanResult | t.CPtr,
|
||
filter_set: str,
|
||
filter_count: int) -> int:
|
||
"""将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器)
|
||
|
||
若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。
|
||
"""
|
||
if temp_dir is None or scan_result is None:
|
||
return 1
|
||
|
||
# 构造路径 temp_dir/_sha1_map.txt
|
||
dir_len: t.CSizeT = string.strlen(temp_dir)
|
||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||
if map_path is None:
|
||
return 1
|
||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||
|
||
# 打开文件写入(CREATE_ALWAYS)
|
||
f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.W)
|
||
if f.closed:
|
||
return 1
|
||
|
||
# 写入每个 include 条目: {sha1}:includes/{rel_path}\n
|
||
entry_size_w: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||
line_buf: bytes = stdlib.malloc(512)
|
||
if line_buf is None:
|
||
f.close()
|
||
return 1
|
||
|
||
written_count: int = 0
|
||
for i in range(scan_result.Count):
|
||
ea_w: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_w
|
||
ent_w: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_w, t.CPtr))
|
||
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None:
|
||
continue
|
||
# 若提供过滤集合,只写入集合中的条目
|
||
if filter_set is not None and filter_count > 0:
|
||
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0:
|
||
continue
|
||
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
|
||
line_len_w: t.CSizeT = string.strlen(line_buf)
|
||
f.write(line_buf, line_len_w)
|
||
written_count += 1
|
||
|
||
f.close()
|
||
return 0
|