修正了一些错误

This commit is contained in:
2026-07-26 20:32:26 +08:00
parent ca7c2120b8
commit 1837339f69
203 changed files with 374300 additions and 2638 deletions

View File

@@ -11,6 +11,7 @@ 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
@@ -39,10 +40,20 @@ _mbuddy: memhub.MemManager | t.CPtr
# 最大 includes SHA1 数(用于过滤)
MAX_INCLUDES_SHA1: t.CDefine = 256
# stub 读取缓冲区大小
STUB_READ_BUF_SIZE: t.CDefine = 262144
STUB_READ_BUF_SIZE: t.CDefine = 1048576
# 最大 includes 条目数
MAX_INCLUDES: t.CDefine = 256
# 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
# Phase1 扫描完成后填充,供所有函数使用
_sha1_store_arr: bytes # SHA1 数组(每个 17 字节MAX_INCLUDES 个)
_sha1_store_mod: bytes # 模块名数组(每个 64 字节MAX_INCLUDES 个)
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节MAX_INCLUDES 个)
_sha1_store_count: int # 条目数
# rel_path 最大长度
MAX_REL_PATH_LEN: t.CDefine = 256
# ============================================================
# _sliced_path - 构建切片路径stdlib.malloc 分配,调用者负责 free
@@ -217,77 +228,31 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
# ============================================================
# _load_includes_sha1_set - 读取 _sha1_map.txt收集 includes/ 开头的 SHA1
# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1
#
# _sha1_map.txt 格式:{sha16}:{rel_path}
# 仅收集 rel_path 以 "includes/" 开头的 SHA1用于过滤 stub 文件,
# 避免加载 TransPyV 自身源文件lib/*)的 stub 导致链接失败。
# 数据来源是 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:
"""读取 _sha1_map.txt将 includes/ 开头的 SHA1 写入 sha1_set
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
pool/temp_dir 参数保留以兼容现有调用者,但不再使用。
数据来源是 PopulateSha1MapStore 填充的全局存储器。
sha1_set 大小为 MAX_INCLUDES_SHA1 * 17每个 SHA1 16字符+null
返回找到的 includes SHA1 数量,-1 表示错误"""
if pool is None or temp_dir is None or sha1_set is None:
if sha1_set is None:
return -1
# 构造路径 temp_dir/_sha1_map.txt使用 stdlib.malloc 避免 mbuddy 池耗尽)
dir_len: t.CSizeT = string.strlen(temp_dir)
map_path: bytes = stdlib.malloc(dir_len + 32)
if map_path is None:
if _sha1_store_arr is None:
return -1
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
# 打开文件
f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R)
if f.closed:
return -1
# 读取内容(使用 stdlib.malloc 避免 mbuddy 池耗尽)
MAP_BUF_SIZE: t.CSizeT = 65536
content: str = stdlib.malloc(MAP_BUF_SIZE)
if content is None:
f.close()
return -1
bytes_read: t.CInt64T = f.read_all(content, MAP_BUF_SIZE)
f.close()
if bytes_read <= 0:
return -1
if bytes_read < MAP_BUF_SIZE:
content[bytes_read] = '\0'
else:
content[MAP_BUF_SIZE - 1] = '\0'
# 解析行: {sha1}:{rel_path}
count: int = 0
pos: t.CSizeT = 0
content_len: t.CSizeT = bytes_read
while pos < content_len:
# 找行尾
line_start: t.CSizeT = pos
while pos < content_len:
if content[pos] == '\n':
break
pos += 1
line_len: t.CSizeT = pos - line_start
pos += 1 # skip \n
# 最小长度: 16(sha1) + 1(:) + 9(includes/) = 26
if line_len < 26:
continue
# 检查第17个字符是 ':'
if content[line_start + 16] != ':':
continue
# 检查 rel_path 是否以 "includes/" 开头
rel_start: t.CSizeT = line_start + 17
if string.strncmp(content + rel_start, "includes/", 9) == 0:
if count < MAX_INCLUDES_SHA1:
string.strncpy(sha1_set + count * 17, content + line_start, 16)
sha1_set[count * 17 + 16] = '\0'
count += 1
for i in range(_sha1_store_count):
if count >= MAX_INCLUDES_SHA1:
break
idx: t.CSizeT = t.CSizeT(i) * 17
string.strcpy(sha1_set + t.CSizeT(count) * 17, _sha1_store_arr + idx)
count += 1
return count
@@ -305,10 +270,10 @@ def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
# ============================================================
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出)
#
# TransPyV.exe 自己生成 _sha1_map.txt不再依赖 Projectrans.py 预生成),
# 确保所有 includes 文件的 SHA1 都被记录,供 StubMerger 加载 stub 时过滤
# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。
# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器
#
# Args:
# mb: 内存池
@@ -322,7 +287,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
scan_result: IncludesScanner.ScanResult | t.CPtr,
filter_set: t.CChar | t.CPtr,
filter_count: int) -> int:
"""将 includes 扫描结果写入 _sha1_map.txt供 StubMerger 使用
"""将 includes 扫描结果写入 _sha1_map.txt人类可读输出,程序内部使用内存存储器
若 filter_set 不为 None 且 filter_count > 0只写入 filter_set 中的条目。
"""
@@ -367,6 +332,212 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
return 0
# ============================================================
# PopulateSha1MapStore - 从扫描结果填充全局 SHA1 映射存储器
#
# 遍历 ScanResult.Entries为每个条目构建 sha1→module_name 映射。
# 模块名通过 _PathToModuleName(includes/rel_path) 计算。
# 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
#
# Args:
# scan_result: IncludesScanner 扫描结果
#
# Returns:
# 填充的条目数,-1 表示失败
# ============================================================
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> int:
"""从扫描结果填充全局 SHA1 映射存储器
遍历 ScanResult.Entries为每个条目构建 sha1→module_name 映射。
模块名通过 _PathToModuleName(includes/rel_path) 计算。
内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
"""
global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count
if scan_result is None:
return -1
# 释放旧存储器
if _sha1_store_arr is not None:
stdlib.free(_sha1_store_arr)
if _sha1_store_mod is not None:
stdlib.free(_sha1_store_mod)
if _sha1_store_rel is not None:
stdlib.free(_sha1_store_rel)
_sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17)
_sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64)
_sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN)
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
return -1
string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17)
string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64)
string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN)
_sha1_store_count = 0
entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
for i in range(scan_result.Count):
if _sha1_store_count >= MAX_INCLUDES:
break
ea: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_p
ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
if ent is None or ent.Sha1 is None or ent.RelPath is None:
continue
# 构建完整路径 includes/{RelPath}_PathToModuleName 需要)
rp_len: t.CSizeT = string.strlen(ent.RelPath)
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
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)
stdlib.free(full_path_buf)
if mod_name is not None:
idx: t.CSizeT = t.CSizeT(_sha1_store_count) * 17
string.strcpy(_sha1_store_arr + idx, ent.Sha1)
idx2: t.CSizeT = t.CSizeT(_sha1_store_count) * 64
mn_len: t.CSizeT = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(_sha1_store_mod + idx2, mod_name)
else:
string.strncpy(_sha1_store_mod + idx2, mod_name, 63)
_sha1_store_mod[idx2 + 63] = '\0'
stdlib.free(mod_name)
# 保存 rel_path供 Phase2 3.5 编译缺失 includes 使用)
idx3: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN
if rp_len < MAX_REL_PATH_LEN:
string.strcpy(_sha1_store_rel + idx3, ent.RelPath)
else:
string.strncpy(_sha1_store_rel + idx3, ent.RelPath, MAX_REL_PATH_LEN - 1)
_sha1_store_rel[idx3 + MAX_REL_PATH_LEN - 1] = '\0'
_sha1_store_count += 1
return _sha1_store_count
# ============================================================
# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器
#
# Phase1 调用 PopulateSha1MapStore 填充 includes 文件,
# Phase2 扫描 App 源文件后调用此函数追加 App 模块条目,
# 确保编译 App 自身时跨模块 CDefine 查找能命中 App 模块
# (如 StubMerger.MAX_INCLUDES 在编译 Phase2 时通过
# _build_source_path_from_sha1 查找 StubMerger 的源路径)。
#
# 若存储器未初始化PopulateSha1MapStore 未调用),自动初始化。
# 若 SHA1 已存在(去重),跳过。
#
# Args:
# sha1: SHA1 字符串16 字符 + null
# rel_path: 相对路径(如 "lib/core/StubMerger.py"
# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
#
# Returns:
# 0 成功含去重跳过1 失败
# ============================================================
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
"""追加条目到全局 SHA1 映射存储器Phase2 用)"""
global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count
if sha1 is None or rel_path is None:
return 1
# 存储器未初始化自动初始化Phase2 可能先于 Phase1 调用)
if _sha1_store_arr is None:
_sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17)
_sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64)
_sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN)
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
return 1
string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17)
string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64)
string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN)
_sha1_store_count = 0
if _sha1_store_count >= MAX_INCLUDES:
return 1
# 去重:若 SHA1 已存在则跳过
for i in range(_sha1_store_count):
sidx_d: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(_sha1_store_arr + sidx_d, sha1) == 0:
return 0
# 模块名rel_path "lib/core/StubMerger.py" → "lib.core.StubMerger"
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
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)
stdlib.free(full_path_buf)
if mod_name is None:
return 1
# 写入 SHA1
idx_a: t.CSizeT = t.CSizeT(_sha1_store_count) * 17
string.strcpy(_sha1_store_arr + idx_a, sha1)
# 写入模块名
idx_m: t.CSizeT = t.CSizeT(_sha1_store_count) * 64
mn_len: t.CSizeT = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(_sha1_store_mod + idx_m, mod_name)
else:
string.strncpy(_sha1_store_mod + idx_m, mod_name, 63)
_sha1_store_mod[idx_m + 63] = '\0'
stdlib.free(mod_name)
# 写入相对路径
idx_r: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN
rp_len: t.CSizeT = string.strlen(rel_path)
if rp_len < MAX_REL_PATH_LEN:
string.strcpy(_sha1_store_rel + idx_r, rel_path)
else:
string.strncpy(_sha1_store_rel + idx_r, rel_path, MAX_REL_PATH_LEN - 1)
_sha1_store_rel[idx_r + MAX_REL_PATH_LEN - 1] = '\0'
_sha1_store_count += 1
return 0
# ============================================================
# GetSha1StoreCount - 获取全局存储器条目数
# ============================================================
def GetSha1StoreCount() -> int:
"""返回全局 SHA1 存储器的条目数"""
return _sha1_store_count
def GetSha1StoreArrPtr() -> bytes | t.CPtr:
"""返回 SHA1 数组指针(每个条目 17 字节)"""
return _sha1_store_arr
def GetSha1StoreModArrPtr() -> bytes | t.CPtr:
"""返回模块名数组指针(每个条目 64 字节)"""
return _sha1_store_mod
def GetSha1StoreRelArrPtr() -> bytes | t.CPtr:
"""返回相对路径数组指针(每个条目 MAX_REL_PATH_LEN 字节)"""
return _sha1_store_rel
# ============================================================
# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用)
#
# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。
# 调用者直接遍历数组sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*MAX_REL_PATH_LEN]
# 内存归全局存储器所有,调用者不要 free
# ============================================================
def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int:
"""获取全局存储器数组指针
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针,
函数将存储器数组指针写入这些位置。
返回条目数,-1 失败
"""
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None:
return -1
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
return -1
sp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_sha1_arr, t.CPtr))
if sp is not None:
sp[0] = _sha1_store_arr
mp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_mod_arr, t.CPtr))
if mp is not None:
mp[0] = _sha1_store_mod
rp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_rel_arr, t.CPtr))
if rp is not None:
rp[0] = _sha1_store_rel
return _sha1_store_count
# ============================================================
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
#
@@ -499,6 +670,39 @@ def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
return 0
# ============================================================
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
#
# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.py
# 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer 子模块类型。
# 当模块名包含 '.' 时,检查其顶层包名是否在 deps_buf 中。
#
# 例如:
# deps_buf = "ast stdio string"
# mod_name = "ast.lexer" → 顶层包 "ast" 在 deps_buf 中 → 返回 1
# mod_name = "ast.token" → 顶层包 "ast" 在 deps_buf 中 → 返回 1
# mod_name = "foo.bar" → 顶层包 "foo" 不在 deps_buf 中 → 返回 0
# ============================================================
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
if deps_buf is None or mod_name is None:
return 0
# 查找第一个 '.',提取顶层包名
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
if dot_pos is None:
return 0
pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(mod_name))
if pkg_len == 0 or pkg_len >= 256:
return 0
# 构造顶层包名(截断到 '.' 之前)
# 直接在 mod_name 上临时截断检查,避免分配
saved_ch: t.CChar = mod_name[pkg_len]
mod_name[pkg_len] = '\0'
result: int = HandlesImports.is_module_imported(deps_buf, mod_name)
mod_name[pkg_len] = saved_ch
return result
# ============================================================
# _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque
#
@@ -806,7 +1010,70 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
in_body = 1
continue
# 跳过其他行global 带初值定义、type 定义等stub 已有 external 声明
# 提取类型定义(%... = type { ... } 或 %... = type < ... >
# stub.ll 中只有 opaque 声明,需要从 text.ll 中提取完整定义
# 用于 alloca 和 getelementptr 等需要完整类型的指令
if ll > 0 and dep_buf[ls] == '%':
eq_td: t.CSizeT = ls + 1
while eq_td < ls + ll:
if dep_buf[eq_td] == '=':
break
eq_td += 1
if eq_td < ls + ll:
# 检查是否为完整定义(= type { 或 = type <
dep_is_full_td: int = 0
if eq_td + 8 <= ls + ll:
if dep_buf[eq_td + 2] == 't' and dep_buf[eq_td + 3] == 'y' \
and dep_buf[eq_td + 4] == 'p' and dep_buf[eq_td + 5] == 'e' \
and dep_buf[eq_td + 6] == ' ':
if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<':
dep_is_full_td = 1
if dep_is_full_td == 1:
# 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀
saved_eq_td: t.CChar = dep_buf[eq_td]
dep_buf[eq_td] = '\0'
# 1. 注释掉 out_buf 中的 opaque 声明(如果有)
_CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls)
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
has_full_td: int = _HasFullTypeDefinition(out_buf, dep_buf + ls)
dep_buf[eq_td] = saved_eq_td
if has_full_td == 0:
# 追加完整类型定义
if out_pos + ll + 2 < out_size:
string.strncpy(out_buf + out_pos, dep_buf + ls, ll)
out_pos += ll
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
continue
# 提取全局变量定义(@... = ...
# 依赖 text.ll 中的全局变量带初值定义(如 @_mbuddy = global ...
# stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义
# 注释掉 out_buf 中的同名行external 声明或旧定义),追加 text.ll 中的完整定义
if ll > 0 and dep_buf[ls] == '@':
eq_gv: t.CSizeT = ls + 1
while eq_gv < ls + ll:
if dep_buf[eq_gv] == '=':
break
eq_gv += 1
if eq_gv < ls + ll:
# 截断到 '=' 之前(不含 '='),得到全局变量名 @name
saved_gv: t.CChar = dep_buf[eq_gv]
dep_buf[eq_gv] = '\0'
# 注释掉 out_buf 中的同名行external 声明或旧定义)
_CommentOutGlobalInBuf(out_buf, dep_buf + ls)
dep_buf[eq_gv] = saved_gv
# 追加完整定义
if out_pos + ll + 2 < out_size:
string.strncpy(out_buf + out_pos, dep_buf + ls, ll)
out_pos += ll
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
continue
# 跳过其他行
return out_pos
@@ -826,13 +1093,14 @@ def _PathToModuleName(path: str) -> str:
if path is None:
return None
plen: t.CSizeT = string.strlen(path)
# 跳过 includes/ 前缀
prefix: str = "includes/"
# 跳过 includes/ 或 includes\ 前缀Windows 反斜杠兼容)
prefix_len: t.CSizeT = 9
path_start: t.CSizeT = 0
if plen > prefix_len and string.strncmp(path, prefix, prefix_len) == 0:
path_start = prefix_len
plen = plen - prefix_len
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
# 去掉 .py 后缀
if plen > 3 and path[path_start + plen - 3] == '.' and path[path_start + plen - 2] == 'p' and path[path_start + plen - 1] == 'y':
plen -= 3
@@ -869,94 +1137,33 @@ def _PathToModuleName(path: str) -> str:
# ============================================================
# _BuildIncludesSha1Map - 从 _sha1_map.txt 构建 sha1 → module_name 映射
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
#
# _sha1_map.txt 格式: {sha1}:includes/{rel_path}\n
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt
# 返回两个数组sha1_list 和 mod_list长度存储在 count 中。
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
# ============================================================
def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
sha1_list: t.CChar | t.CPtr,
mod_list: t.CChar | t.CPtr) -> int:
""" _sha1_map.txt 构建 sha1→module_name 映射,返回条目数"""
if temp_dir is None or sha1_list is None or mod_list is None:
return 0
"""全局内存存储器复制 sha1→module_name 映射到调用者数组
map_path: bytes = stdlib.malloc(td_len + 32)
if map_path is None:
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
数据来源是 PopulateSha1MapStore 填充的全局存储器。
"""
if sha1_list is None or mod_list is None:
return 0
viperlib.snprintf(map_path, td_len + 32, "%s/_sha1_map.txt", temp_dir)
mf: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R)
if mf.closed:
stdlib.free(map_path)
if _sha1_store_arr is None or _sha1_store_mod is None:
return 0
map_buf: bytes = stdlib.malloc(STUB_READ_BUF_SIZE)
if map_buf is None:
mf.close()
stdlib.free(map_path)
return 0
map_br: t.CInt64T = mf.read_all(map_buf, STUB_READ_BUF_SIZE)
mf.close()
stdlib.free(map_path)
if map_br <= 0:
stdlib.free(map_buf)
return 0
if map_br < STUB_READ_BUF_SIZE:
map_buf[map_br] = '\0'
else:
map_buf[STUB_READ_BUF_SIZE - 1] = '\0'
count: int = 0
pos: t.CSizeT = 0
# 在循环外分配 sha1 缓冲区,避免反复 malloc/free 导致堆碎片化
sha1: t.CChar | t.CPtr = stdlib.malloc(17)
if sha1 is None:
stdlib.free(map_buf)
return 0
while pos < map_br and count < MAX_INCLUDES:
# 提取 SHA116 hex
string.strncpy(sha1, map_buf + pos, 16)
sha1[16] = '\0'
pos += 16
# 跳过 ':'
if pos < map_br and map_buf[pos] == ':':
pos += 1
# 提取路径(到 \r、\n 或 \0
# 注意: Windows CRLF 换行符是 \r\n必须同时检查 \r 避免路径包含 \r
path_start: t.CSizeT = pos
while pos < map_br and map_buf[pos] != '\n' and map_buf[pos] != '\r' and map_buf[pos] != '\0':
pos += 1
path_len: t.CSizeT = pos - path_start
# 跳过 \r\n 或 \n
if pos < map_br and map_buf[pos] == '\r':
pos += 1
if pos < map_br and map_buf[pos] == '\n':
pos += 1
if path_len > 0:
# 仅收集 includes/ 前缀的条目(用户文件如 App/ 不应进入 includes 映射)
# 否则用户文件会被误判为 includes导致 is_module_imported 模块名不匹配而跳过加载
if path_len >= 9 and string.strncmp(map_buf + path_start, "includes/", 9) == 0:
path_buf: t.CChar | t.CPtr = stdlib.malloc(path_len + 1)
if path_buf is not None:
string.strncpy(path_buf, map_buf + path_start, path_len)
path_buf[path_len] = '\0'
mod_name: str = _PathToModuleName(path_buf)
stdlib.free(path_buf)
if mod_name is not None:
# 存储 sha1 和 mod_name 到数组中
idx: t.CSizeT = t.CSizeT(count) * 17
string.strcpy(sha1_list + idx, sha1)
idx2: t.CSizeT = t.CSizeT(count) * 64
mn_len: t.CSizeT = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(mod_list + idx2, mod_name)
else:
string.strncpy(mod_list + idx2, mod_name, 63)
mod_list[idx2 + 63] = '\0'
stdlib.free(mod_name)
count += 1
stdlib.free(sha1)
stdlib.free(map_buf)
for i in range(_sha1_store_count):
if count >= MAX_INCLUDES:
break
idx: t.CSizeT = t.CSizeT(i) * 17
idx2: t.CSizeT = t.CSizeT(i) * 64
string.strcpy(sha1_list + t.CSizeT(count) * 17, _sha1_store_arr + idx)
string.strcpy(mod_list + t.CSizeT(count) * 64, _sha1_store_mod + idx2)
count += 1
return count
@@ -977,16 +1184,33 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
# ============================================================
def _FindSha1ByModName(sha1_arr: t.CChar | t.CPtr, mod_arr: t.CChar | t.CPtr,
count: int, mod_name: str) -> str:
"""从模块名查找 SHA1精确匹配"""
"""从模块名查找 SHA1精确匹配 + 包名回退)
精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
支持 import ast→ ast.__init__ 的 SHA1 查找。
"""
if sha1_arr is None or mod_arr is None or mod_name is None:
return None
if count <= 0:
return None
# 第一遍:精确匹配
for i in range(count):
idx: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(mod_arr + idx, mod_name) == 0:
sidx: t.CSizeT = t.CSizeT(i) * 17
return sha1_arr + sidx
# 第二遍:包名回退("ast" → "ast.__init__"
nm_len: t.CSizeT = string.strlen(mod_name)
init_buf: bytes = stdlib.malloc(nm_len + 12)
if init_buf is not None:
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
for i in range(count):
idx2: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(mod_arr + idx2, init_buf) == 0:
sidx2: t.CSizeT = t.CSizeT(i) * 17
stdlib.free(init_buf)
return sha1_arr + sidx2
stdlib.free(init_buf)
return None
@@ -1027,13 +1251,13 @@ def _AddSha1ToSet(set_buf: t.CChar | t.CPtr, count: int, sha1: str) -> int:
#
# 算法(工作列表):
# 1. 扫描 source_dir 下的 .py 文件,解析 AST 获取 _imported_modules
# 2. 构建 SHA1→module_name 映射(从 _sha1_map.txt
# 2. 构建 SHA1→module_name 映射(从全局内存存储器
# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名
#
# Args:
# mb: 内存池
# source_dir: 源文件目录Config.SourceDir
# temp_dir: 临时目录(_sha1_map.txt 和 .deps.txt 所在位置)
# temp_dir: 临时目录(.deps.txt 所在位置_sha1_map.txt 仅人类可读输出,不读取
# reachable_set: 输出参数,可达 SHA1 集合缓冲区MAX_INCLUDES_SHA1 * 17 字节)
#
# Returns:
@@ -1060,11 +1284,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
return -1
map_count: int = _BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr)
if map_count <= 0:
stdio.printf("[Reachable] 无法构建 SHA1 映射\n")
VLogger.error("无法构建 SHA1 映射", "Reachable")
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
stdio.printf("[Reachable] SHA1 映射: %d\n", map_count)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "SHA1 映射: %d", map_count)
VLogger.debug(fb, "Reachable")
# 2. 扫描 source_dir 下的 .py 文件,收集直接依赖
dir_len: t.CSizeT = string.strlen(source_dir)
@@ -1099,7 +1326,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
if handle == win32base.INVALID_HANDLE_VALUE:
stdio.printf("[Reachable] 未找到 .py 文件: %s\n", pattern)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "未找到 .py 文件: %s", pattern)
VLogger.warning(fb, "Reachable")
stdlib.free(pattern)
stdlib.free(find_data)
stdlib.free(sha1_arr)
@@ -1176,7 +1406,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
worklist[wl_len - 1] = '\0'
wl_len -= 1
stdio.printf("[Reachable] 源文件直接依赖: '%s'\n", worklist)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "源文件直接依赖: '%s'", worklist)
VLogger.debug(fb, "Reachable")
# 2.5. 全局预加入基础容器模块_list/_dict/json
#
@@ -1223,7 +1456,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
stdio.printf("[Reachable] 预加入容器模块后: '%s'\n", worklist)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist)
VLogger.debug(fb, "Reachable")
# 3. 工作列表算法:递归收集可达 SHA1
processed: bytes = stdlib.malloc(8192)
@@ -1261,13 +1497,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
if HandlesImports.is_module_imported(processed, mod_buf) != 0:
already = 1
if already == 0:
# 加入 processed
if proc_len + name_len + 1 < 8192:
string.strcpy(processed + proc_len, mod_buf)
proc_len += name_len
processed[proc_len] = ' '
proc_len += 1
processed[proc_len] = '\0'
# 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug
if processed is not None:
if proc_len + name_len + 1 < 8192:
string.strcpy(processed + proc_len, mod_buf)
proc_len = proc_len + name_len
processed[proc_len] = ' '
proc_len = proc_len + 1
processed[proc_len] = '\0'
# 查找 SHA1
found_sha1: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf)
@@ -1289,17 +1526,18 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
deps_buf[dbr] = '\0'
else:
deps_buf[2047] = '\0'
# 追加到 worklist确保前面有空格分隔符,避免模块名合并
# 追加到 worklist嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug
dl: t.CSizeT = string.strlen(deps_buf)
if dl > 0 and wl_len + dl + 2 < 8192:
if wl_len > 0 and worklist[wl_len - 1] != ' ':
if worklist is not None:
if dl > 0 and wl_len + dl + 2 < 8192:
if wl_len > 0 and worklist[wl_len - 1] != ' ':
worklist[wl_len] = ' '
wl_len = wl_len + 1
string.strcpy(worklist + wl_len, deps_buf)
wl_len = wl_len + dl
worklist[wl_len] = ' '
wl_len += 1
string.strcpy(worklist + wl_len, deps_buf)
wl_len += dl
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
wl_len = wl_len + 1
worklist[wl_len] = '\0'
stdlib.free(deps_buf)
else:
df.close()
@@ -1310,15 +1548,19 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
stdlib.free(worklist)
stdlib.free(processed)
stdio.printf("[Reachable] 可达 SHA1: %d\n", reachable_count)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "可达 SHA1: %d", reachable_count)
VLogger.debug(fb, "Reachable")
return reachable_count
# ============================================================
# BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR
#
# 按需加载:读取 deps.txt 获取导入模块名,通过 _sha1_map.txt 查找 SHA1
# 按需加载:读取 deps.txt 获取导入模块名,通过内存存储器查找 SHA1
# 仅加载实际使用的依赖 stub而非全部扫描
# _sha1_map.txt 仅作为人类可读输出,程序内部不读取。
# ============================================================
def BuildCombinedIR(temp_dir: str, local_sha1: str,
out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT:
@@ -1387,7 +1629,7 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
# 2. 按需加载依赖 stubs根据 deps.txt 过滤,而非扫描全部)
# 先加载依赖 stub含 type 定义),确保 type 定义在本地 text.ll 的 define 块之前
# 2a. 构建 includes SHA1 → module_name 映射(从 _sha1_map.txt
# 2a. 构建 includes SHA1 → module_name 映射(从全局内存存储器
sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17)
mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64)
inc_count: int = 0
@@ -1436,8 +1678,17 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
is_include = 1
if deps_loaded != 0:
idx_mi: t.CSizeT = t.CSizeT(ii) * 64
if HandlesImports.is_module_imported(deps_buf, mod_arr + idx_mi) != 0:
dep_mod_name: str = mod_arr + idx_mi
if HandlesImports.is_module_imported(deps_buf, dep_mod_name) != 0:
should_load = 1
else:
# 传递性依赖deps.txt 只记录直接导入(如 ast
# 但包的 __init__.py 内部 from .lexer import Lexer
# 会让 IR 引用 ast.lexer 子模块类型。
# 检查 dep_mod_name 是否是已导入包的子模块
# (如 ast.lexer 是 ast 的子模块)
if _is_submodule_of_imported(deps_buf, dep_mod_name) != 0:
should_load = 1
break
if is_include == 0:
# 非 includes stub用户文件总是加载
@@ -1492,7 +1743,10 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
if chk_path is not None:
chk_f: fileio.File | t.CPtr = fileio.File(chk_path, fileio.MODE.R)
if chk_f.closed:
stdio.printf("[FATAL][BuildCombinedIR] 依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译\n", mod_nm, found_s, chk_path)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译", mod_nm, found_s, chk_path)
VLogger.error(fb, "BuildCombinedIR")
sys.exit(1)
chk_f.close()
stdlib.free(chk_path)