Files
TransPyV/App/lib/core/StubMerger.py
2026-07-26 20:33:17 +08:00

1885 lines
82 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import memhub
import string
import stdio
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.Projectrans.Config as Config
# ============================================================
# StubMerger - stub 声明合并模块
#
# 从 temp 目录加载所有 .stub.ll 文件,将声明内容合并到主 IR。
# 这是 Phase2 的核心功能:翻译主文件时,将所有依赖的 stub
# 声明合并到主 IR 前面,使链接器能解析跨模块符号引用。
#
# 合并策略:
# 1. 扫描 temp 目录,收集所有 {sha1}.stub.ll 文件
# 2. 读取每个 stub 文件内容
# 3. 过滤去重(避免重复声明)
# 4. 将声明插入到主 IR 的 target triple 之后
# ============================================================
# 全局 mbuddy 指针
_mbuddy: memhub.MemManager | t.CPtr
# 最大 includes SHA1 数(用于过滤)
MAX_INCLUDES_SHA1: t.CDefine = 256
# stub 读取缓冲区大小
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
#
# 根据 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.freeNone 失败
# ============================================================
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)
# 子目录前缀缓冲区(最多 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'
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'
# 确保子目录存在: {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)
stdlib.free(subdir_buf)
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:
# 构建子目录路径并递归扫描
fname_len: t.CSizeT = string.strlen(fname)
sub_dir: str = stdlib.malloc(bd_len + fname_len + 2)
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)
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 _sha1_store_arr is None:
return -1
count: int = 0
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
# ============================================================
# _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: t.CChar | t.CPtr,
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
# ============================================================
# 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
#
# 逐字节strncmp搜索 func_sig如 @"sha1.func"(/ @printf( ),每次
# 找到匹配后检查行首是否以 "declare " 或 "define " 开头,跳过 call 语句。
# 避免 strstr 子串匹配误匹配 call ... @"func"(...) 中的 @"func"( 子串。
#
# Args:
# out_buf: 已有的 IR 文本缓冲区
# func_sig: 函数签名片段,如 @"sha1.func"(/ @printf(
#
# Returns:
# 1=已存在 declare/define, 0=不存在
# ============================================================
def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int:
"""检查 out_buf 中是否已有函数的 declare 或 define 行"""
if out_buf is None or func_sig is None:
return 0
sig_len: t.CSizeT = string.strlen(func_sig)
out_len: t.CSizeT = string.strlen(out_buf)
if sig_len == 0 or out_len == 0 or sig_len > out_len:
return 0
pos: t.CSizeT = 0
while pos + sig_len <= out_len:
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
ls: t.CSizeT = pos
while ls > 0 and out_buf[ls - 1] != '\n':
ls -= 1
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
return 1
if string.strncmp(out_buf + ls, "define ", 7) == 0:
return 1
pos += 1
return 0
# ============================================================
# _CommentOutDeclareInBuf - 将 out_buf 中匹配 func_sig 的 declare 行注释掉
#
# 用于解决 declare/define 冲突:当本地 text.ll 包含 define 而依赖
# text.ll 已生成了同函数的 declare 时,将 declare 行首字符改为 ';'
# 使其成为注释,然后追加 define 行。
# ============================================================
def _CommentOutDeclareInBuf(out_buf: bytes, func_sig: str) -> int:
"""在 out_buf 中搜索 declare 行(包含 func_sig将其注释掉"""
if out_buf is None or func_sig is None:
return 0
sig_len: t.CSizeT = string.strlen(func_sig)
out_len: t.CSizeT = string.strlen(out_buf)
if sig_len == 0 or out_len == 0 or sig_len > out_len:
return 0
pos: t.CSizeT = 0
while pos + sig_len <= out_len:
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
ls: t.CSizeT = pos
while ls > 0 and out_buf[ls - 1] != '\n':
ls -= 1
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
out_buf[ls] = ';'
return 1
pos += 1
return 0
# ============================================================
# _CommentOutGlobalInBuf - 将 out_buf 中匹配全局变量名的行注释掉
#
# 解决 stub/text 全局变量重复定义问题stub 中可能是 external 声明
# 或完整定义,以 text 中的定义为准,将 stub 中的同名行注释掉。
# ============================================================
def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int:
"""在 out_buf 中搜索全局变量定义行(以 global_name 开头),将行首改为 ';'"""
if out_buf is None or global_name is None:
return 0
name_len: t.CSizeT = string.strlen(global_name)
out_len: t.CSizeT = string.strlen(out_buf)
if name_len == 0 or out_len == 0 or name_len > out_len:
return 0
pos: t.CSizeT = 0
while pos + name_len <= out_len:
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
# 确认是行首(前一个是换行或 pos==0
if pos == 0 or out_buf[pos - 1] == '\n':
out_buf[pos] = ';'
return 1
pos += 1
return 0
# ============================================================
# _CommentOutOpaqueTypeInBuf - 将 out_buf 中匹配类型名的 opaque 声明行注释掉
#
# 解决跨模块类型定义冲突:本地 stub 可能包含自动生成的 opaque 声明
#(如 %"sha1.Widget" = type opaque而依赖 stub 包含完整定义
#(如 %"sha1.Widget" = type { i8*, i32 })。去重时需要将 opaque 声明
# 注释掉,让完整定义被追加,否则 LLC 报错 "Cannot allocate unsized type"。
# ============================================================
def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
"""在 out_buf 中搜索 type_name_prefix 对应的 opaque 声明行,将行首改为 ';'
type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=' 和后续内容)
返回 1=已注释, 0=未找到
"""
if out_buf is None or type_name_prefix is None:
return 0
prefix_len: t.CSizeT = string.strlen(type_name_prefix)
out_len: t.CSizeT = string.strlen(out_buf)
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
return 0
pos: t.CSizeT = 0
while pos + prefix_len <= out_len:
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
# 确认是行首(前一个是换行或 pos==0
if pos == 0 or out_buf[pos - 1] == '\n':
# 检查后面是否是 " = type opaque"
p: t.CSizeT = pos + prefix_len
# 跳过空格
while p < out_len and out_buf[p] == ' ':
p += 1
if p < out_len and out_buf[p] == '=':
p += 1
while p < out_len and out_buf[p] == ' ':
p += 1
if p + 11 <= out_len:
if string.strncmp(out_buf + p, "type opaque", 11) == 0:
# 找到 opaque 声明,将行首改为 ';'
out_buf[pos] = ';'
return 1
pos += 1
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
#
# type_name_prefix 格式: '%"sha1.ClassName"'(不含 '='
# 完整定义: %"name" = type { ... } 或 %"name" = type < { ... } >
# 返回 1=已有完整定义, 0=无完整定义
# ============================================================
def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int:
"""检查 out_buf 中是否已有 type_name_prefix 的完整定义"""
if out_buf is None or type_name_prefix is None:
return 0
prefix_len: t.CSizeT = string.strlen(type_name_prefix)
out_len: t.CSizeT = string.strlen(out_buf)
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
return 0
pos: t.CSizeT = 0
while pos + prefix_len <= out_len:
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
# 确认是行首(前一个是换行或 pos==0且未被注释行首不是 ';'
is_line_start: int = 0
if pos == 0:
is_line_start = 1
elif out_buf[pos - 1] == '\n':
is_line_start = 1
if is_line_start == 1:
# 检查后面是否是 " = type {" 或 " = type <"
p: t.CSizeT = pos + prefix_len
while p < out_len and out_buf[p] == ' ':
p += 1
if p < out_len and out_buf[p] == '=':
p += 1
while p < out_len and out_buf[p] == ' ':
p += 1
if p + 5 < out_len:
if string.strncmp(out_buf + p, "type ", 5) == 0:
p += 5
if p < out_len:
if out_buf[p] == '{' or out_buf[p] == '<':
return 1
pos += 1
return 0
# ============================================================
# _LoadAndAppendStub - 加载依赖 stub 并追加到 out_buf
#
# 读取 {temp_dir}/{sha1}.stub.ll跳过 header;, target, source_filename
# 去重 declare/global追加到 out_buf。返回新 out_pos。
# ============================================================
def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT,
out_pos: t.CSizeT) -> t.CSizeT:
"""加载依赖 stub 并追加(跳过 header去重"""
if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None:
return out_pos
dep_path: str = _sliced_path(temp_dir, td_len, dep_sha1, "stub.ll")
if dep_path is None:
return out_pos
df_ls: fileio.File | t.CPtr = fileio.File(dep_path, fileio.MODE.R)
if df_ls.closed:
stdlib.free(dep_path)
return out_pos
dep_br_ls: t.CInt64T = df_ls.read_all(dep_buf, STUB_READ_BUF_SIZE)
df_ls.close()
stdlib.free(dep_path)
if dep_br_ls <= 0:
return out_pos
if dep_br_ls < STUB_READ_BUF_SIZE:
dep_buf[dep_br_ls] = '\0'
else:
dep_buf[STUB_READ_BUF_SIZE - 1] = '\0'
# 注释掉依赖 stub 中的字符串常量行(模块内部的,不需要)
# 简单方案:搜索 "= external unnamed_addr constant" 并将行首的 @ 改为 ;@
cfs_pos: t.CSizeT = 0
while cfs_pos + 30 < t.CSizeT(dep_br_ls):
ext_p: t.CPtr = string.strstr(dep_buf + cfs_pos, "= external unnamed_addr constant")
if ext_p is None:
break
ext_off2: t.CSizeT = t.CSizeT(t.CUInt64T(ext_p) - t.CUInt64T(dep_buf))
# 向前查找行首的 @
ls: t.CSizeT = ext_off2
while ls > 0:
if dep_buf[ls] == '\n':
ls += 1
break
ls -= 1
if ls < ext_off2 and dep_buf[ls] == '@':
dep_buf[ls] = ';'
cfs_pos = ext_off2 + 30
# 跳过 header追加内容去重 declare/global
dep_pos: t.CSizeT = 0
skipping: int = 1
while dep_pos < dep_br_ls:
line_start: t.CSizeT = dep_pos
while dep_pos < dep_br_ls:
if dep_buf[dep_pos] == '\n':
break
dep_pos += 1
line_len: t.CSizeT = dep_pos - line_start
if dep_pos < dep_br_ls:
dep_pos += 1
if skipping != 0:
if line_len == 0:
continue
ch0: int = dep_buf[line_start]
if ch0 == ';':
continue
if ch0 == 't' and line_len >= 6:
if string.strncmp(dep_buf + line_start, "target", 6) == 0:
continue
if ch0 == 's' and line_len >= 15:
if string.strncmp(dep_buf + line_start, "source_filename", 15) == 0:
continue
skipping = 0
# 去重declare用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句)
if line_len >= 8 and string.strncmp(dep_buf + line_start, "declare ", 8) == 0:
at: t.CSizeT = line_start + 8
while at < line_start + line_len:
if dep_buf[at] == '@':
break
at += 1
if at < line_start + line_len:
lp: t.CSizeT = at + 1
while lp < line_start + line_len:
if dep_buf[lp] == '(':
break
lp += 1
if lp < line_start + line_len:
saved: t.CChar = dep_buf[lp + 1]
dep_buf[lp + 1] = '\0'
if _IsFuncDeclaredOrDefined(out_buf, dep_buf + at) != 0:
dep_buf[lp + 1] = saved
continue
dep_buf[lp + 1] = saved
# 去重external global
if line_len > 0 and dep_buf[line_start] == '@':
eq: t.CSizeT = line_start + 1
while eq < line_start + line_len:
if dep_buf[eq] == '=':
break
eq += 1
if eq < line_start + line_len:
saved: t.CChar = dep_buf[eq + 1]
dep_buf[eq + 1] = '\0'
if string.strstr(out_buf, dep_buf + line_start) is not None:
dep_buf[eq + 1] = saved
continue
dep_buf[eq + 1] = saved
# 去重type 定义(如 %"sha1.Point" = type {i32, i32}
# 区分完整定义(= type { ... } / = type < ... >)和 opaque 声明(= type opaque
# 当本地 stub 有 opaque 声明而依赖 stub 有完整定义时,注释掉 opaque 行,
# 让完整定义被追加,避免 LLC "Cannot allocate unsized type" 错误。
if line_len > 0 and dep_buf[line_start] == '%':
eq2: t.CSizeT = line_start + 1
while eq2 < line_start + line_len:
if dep_buf[eq2] == '=':
break
eq2 += 1
if eq2 < line_start + line_len:
saved2: t.CChar = dep_buf[eq2 + 1]
dep_buf[eq2 + 1] = '\0'
# 检查依赖行是否为完整定义(= type { 或 = type <
dep_is_full_def: int = 0
if eq2 + 8 <= line_start + line_len:
if dep_buf[eq2 + 2] == 't' and dep_buf[eq2 + 3] == 'y' and dep_buf[eq2 + 4] == 'p' and dep_buf[eq2 + 5] == 'e' and dep_buf[eq2 + 6] == ' ':
if dep_buf[eq2 + 7] == '{' or dep_buf[eq2 + 7] == '<':
dep_is_full_def = 1
if dep_is_full_def == 1:
# 依赖行是完整定义
# 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀
saved_eq: t.CChar = dep_buf[eq2]
dep_buf[eq2] = '\0'
# 1. 注释掉 out_buf 中的 opaque 声明(如果有)
commented: int = _CommentOutOpaqueTypeInBuf(out_buf, dep_buf + line_start)
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
has_full: int = _HasFullTypeDefinition(out_buf, dep_buf + line_start)
dep_buf[eq2] = saved_eq
if has_full == 1:
dep_buf[eq2 + 1] = saved2
continue
# 3. 追加完整定义
else:
# 依赖行是 opaque 声明,用原有子串匹配去重
found_opaque: t.CPtr = string.strstr(out_buf, dep_buf + line_start)
if found_opaque is not None:
dep_buf[eq2 + 1] = saved2
continue
dep_buf[eq2 + 1] = saved2
# 追加
if out_pos + line_len + 2 < out_size:
string.strncpy(out_buf + out_pos, dep_buf + line_start, line_len)
out_pos += line_len
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
return out_pos
# ============================================================
# _LoadAndAppendTextDeclares - 从依赖 text.ll 提取 declare 追加到 out_buf
#
# 读取 {temp_dir}/{sha1}.text.ll将 define 行转为 declare提取函数签名
# 跳过函数体,去重后追加到 out_buf。返回新 out_pos。
# ============================================================
def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT,
out_pos: t.CSizeT) -> t.CSizeT:
"""从依赖 text.ll 提取 declaredefine 转 declare追加到 out_buf"""
if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None:
return out_pos
text_path: str = _sliced_path(temp_dir, td_len, dep_sha1, "text.ll")
if text_path is None:
return out_pos
tf_lt: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R)
if tf_lt.closed:
stdlib.free(text_path)
return out_pos
text_br: t.CInt64T = tf_lt.read_all(dep_buf, STUB_READ_BUF_SIZE)
tf_lt.close()
stdlib.free(text_path)
if text_br <= 0:
return out_pos
if text_br < STUB_READ_BUF_SIZE:
dep_buf[text_br] = '\0'
else:
dep_buf[STUB_READ_BUF_SIZE - 1] = '\0'
dep_pos_lt: t.CSizeT = 0
in_body: int = 0
while dep_pos_lt < text_br:
ls: t.CSizeT = dep_pos_lt
while dep_pos_lt < text_br:
if dep_buf[dep_pos_lt] == '\n':
break
dep_pos_lt += 1
ll: t.CSizeT = dep_pos_lt - ls
if dep_pos_lt < text_br:
dep_pos_lt += 1
# 函数体跳过:直到单独的 } 行
if in_body != 0:
if ll == 1 and dep_buf[ls] == '}':
in_body = 0
continue
# 跳过空行和 header
if ll == 0:
continue
c0: int = dep_buf[ls]
if c0 == ';':
continue
if c0 == 't' and ll >= 6 and string.strncmp(dep_buf + ls, "target", 6) == 0:
continue
if c0 == 's' and ll >= 15 and string.strncmp(dep_buf + ls, "source_filename", 15) == 0:
continue
# 处理 define 行:转为 declare
if ll >= 7 and string.strncmp(dep_buf + ls, "define ", 7) == 0:
# 提取 @funcname( 用于去重
at_lt: t.CSizeT = ls + 7
while at_lt < ls + ll:
if dep_buf[at_lt] == '@':
break
at_lt += 1
if at_lt >= ls + ll:
in_body = 1
continue
lp_lt: t.CSizeT = at_lt + 1
while lp_lt < ls + ll:
if dep_buf[lp_lt] == '(':
break
lp_lt += 1
if lp_lt >= ls + ll:
in_body = 1
continue
# 去重检查:用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句
saved_lt: t.CChar = dep_buf[lp_lt + 1]
dep_buf[lp_lt + 1] = '\0'
if _IsFuncDeclaredOrDefined(out_buf, dep_buf + at_lt) != 0:
dep_buf[lp_lt + 1] = saved_lt
in_body = 1
continue
dep_buf[lp_lt + 1] = saved_lt
# 找行尾的 ' {' 并截断
decl_end: t.CSizeT = ls + ll
if decl_end > ls + 1:
if dep_buf[decl_end - 1] == '{' and dep_buf[decl_end - 2] == ' ':
decl_end -= 2
# 输出: declare + content (skip "define " 7 chars, up to decl_end)
copy_len_lt: t.CSizeT = decl_end - (ls + 7)
if out_pos + 8 + copy_len_lt + 2 < out_size:
string.strncpy(out_buf + out_pos, "declare ", 8)
out_pos += 8
string.strncpy(out_buf + out_pos, dep_buf + ls + 7, copy_len_lt)
out_pos += copy_len_lt
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
in_body = 1
continue
# 提取类型定义(%... = 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
# ============================================================
# _PathToModuleName - 将文件路径转换为模块名
#
# 输入: "includes/stdio.py" 或 "includes/lib/core/Handles/HandlesType.py"
# 输出: "stdio" 或 "lib.core.Handles.HandlesType"
# 去除 includes/ 前缀和 .py 后缀,/ 替换为 .
# 返回 stdlib.malloc 分配的字符串,调用者负责释放
# ============================================================
def _PathToModuleName(path: str) -> str:
"""将文件路径转换为模块名
特殊处理 __init__.pyincludes/ast/__init__.py → 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
# 去掉 .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
if plen == 0:
return None
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.py
# /__init__ 是 9 个字符:/ _ _ i n i t _ _
# 如果是,去掉分隔符+__init__ 后缀,模块名就是包名(如 ast/__init__ → ast
# 注意:顶级 __init__.pyplen=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 映射
#
# 数据来源是 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→module_name 映射到调用者数组
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
数据来源是 PopulateSha1MapStore 填充的全局存储器。
"""
if sha1_list is None or mod_list is None:
return 0
if _sha1_store_arr is None or _sha1_store_mod is None:
return 0
count: int = 0
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
# ============================================================
# _FindSha1ByModName - 从模块名查找 SHA1精确匹配
#
# 在 sha1_arr/mod_arr 映射中查找模块名对应的 SHA1。
# 用于依赖图分析:模块名 → SHA1。
#
# Args:
# sha1_arr: SHA1 数组(每个 17 字节)
# mod_arr: 模块名数组(每个 64 字节)
# count: 映射条目数
# mod_name: 要查找的模块名
#
# Returns:
# SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None
# ============================================================
def _FindSha1ByModName(sha1_arr: t.CChar | t.CPtr, mod_arr: t.CChar | t.CPtr,
count: int, mod_name: str) -> str:
"""从模块名查找 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
# ============================================================
# _AddSha1ToSet - 将 SHA1 加入集合(去重)
#
# 检查 SHA1 是否已在集合中,若不在则加入。返回新的 count。
#
# Args:
# set_buf: SHA1 集合缓冲区(每个 17 字节)
# count: 当前集合中的 SHA1 数量
# sha1: 要加入的 SHA1
#
# Returns:
# 新的 count如果已存在则不变
# ============================================================
def _AddSha1ToSet(set_buf: t.CChar | t.CPtr, count: int, sha1: str) -> int:
"""将 SHA1 加入集合,返回新 count"""
if set_buf is None or sha1 is None:
return count
for i in range(count):
idx: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(set_buf + idx, sha1) == 0:
return count
if count < MAX_INCLUDES_SHA1:
idx: t.CSizeT = t.CSizeT(count) * 17
string.strcpy(set_buf + idx, sha1)
return count + 1
return count
# ============================================================
# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需翻译)
#
# 从 source_dir 的源文件开始,解析 import 语句,递归收集可达的
# includes 文件 SHA1。Phase 1b 只翻译可达集合中的文件,避免
# 翻译不需要的 includes如 Test 不依赖 llvmlite则不翻译
#
# 算法(工作列表):
# 1. 扫描 source_dir 下的 .py 文件,解析 AST 获取 _imported_modules
# 2. 构建 SHA1→module_name 映射(从全局内存存储器)
# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名
#
# Args:
# mb: 内存池
# source_dir: 源文件目录Config.SourceDir
# temp_dir: 临时目录(.deps.txt 所在位置_sha1_map.txt 仅人类可读输出,不读取)
# reachable_set: 输出参数,可达 SHA1 集合缓冲区MAX_INCLUDES_SHA1 * 17 字节)
#
# Returns:
# 可达 SHA1 数量(>0 成功,<=0 失败)
# ============================================================
def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
temp_dir: str,
reachable_set: t.CChar | t.CPtr) -> int:
"""构建可达 SHA1 集合(依赖图按需翻译)"""
if source_dir is None or temp_dir is None or reachable_set is None:
return -1
SRC_BUF_SIZE_R: t.CSizeT = 1048576
# 1. 构建 SHA1→module_name 映射
td_len_r: t.CSizeT = string.strlen(temp_dir)
sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17)
mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64)
if sha1_arr is None or mod_arr is None:
if sha1_arr is not None:
stdlib.free(sha1_arr)
if mod_arr is not None:
stdlib.free(mod_arr)
return -1
map_count: int = _BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr)
if map_count <= 0:
VLogger.error("无法构建 SHA1 映射", "Reachable")
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
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)
pattern: bytes = stdlib.malloc(dir_len + 8)
if pattern is None:
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
viperlib.snprintf(pattern, dir_len + 8, "%s/*.py", source_dir)
find_data_size: t.CSizeT = win32file.WIN32_FIND_DATAA.__sizeof__()
find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(find_data_size + 16)
if find_data is None:
stdlib.free(pattern)
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
string.memset(find_data, 0, find_data_size + 16)
# 工作列表(空格分隔的模块名)
worklist: bytes = stdlib.malloc(8192)
if worklist is None:
stdlib.free(pattern)
stdlib.free(find_data)
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
worklist[0] = '\0'
wl_len: t.CSizeT = 0
reachable_count: int = 0
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
if handle == win32base.INVALID_HANDLE_VALUE:
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)
stdlib.free(mod_arr)
stdlib.free(worklist)
return -1
while True:
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
if 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'
# 释放 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:
break
win32base.FindClose(handle)
stdlib.free(pattern)
stdlib.free(find_data)
# 去掉末尾多余空格
if wl_len > 0 and worklist[wl_len - 1] == ' ':
worklist[wl_len - 1] = '\0'
wl_len -= 1
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
#
# 这些模块定义了 list/dict 等内建容器的泛型模板。
# 使用 list[...]/dict[...] 语法时需要这些模块被翻译并注册模板。
# 即使源文件和 includes 文件没有显式 import _list也预加入以确保可达。
# 这对应 TPC 的 _inject_auto_imports 机制TPC 在翻译前自动注入 import _list
# _list
if HandlesImports.is_module_imported(worklist, "_list") == 0:
bsha1_l: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, "_list")
if bsha1_l is not None:
if wl_len + 7 < 8192:
if wl_len > 0:
worklist[wl_len] = ' '
wl_len += 1
string.strcpy(worklist + wl_len, "_list")
wl_len += 5
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
# _dict
if HandlesImports.is_module_imported(worklist, "_dict") == 0:
bsha1_d: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, "_dict")
if bsha1_d is not None:
if wl_len + 7 < 8192:
if wl_len > 0:
worklist[wl_len] = ' '
wl_len += 1
string.strcpy(worklist + wl_len, "_dict")
wl_len += 5
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
# json
if HandlesImports.is_module_imported(worklist, "json") == 0:
bsha1_j: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, "json")
if bsha1_j is not None:
if wl_len + 6 < 8192:
if wl_len > 0:
worklist[wl_len] = ' '
wl_len += 1
string.strcpy(worklist + wl_len, "json")
wl_len += 4
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
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)
if processed is None:
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
stdlib.free(worklist)
return -1
processed[0] = '\0'
proc_len: t.CSizeT = 0
wl_pos: t.CSizeT = 0
wl_iter: int = 0
while wl_pos < wl_len:
wl_iter += 1
# 提取一个模块名(到空格或末尾)
name_start: t.CSizeT = wl_pos
while wl_pos < wl_len and worklist[wl_pos] != ' ':
wl_pos += 1
name_len: t.CSizeT = wl_pos - name_start
if wl_pos < wl_len:
wl_pos += 1
if name_len == 0:
continue
# 复制模块名
mod_buf: bytes = stdlib.malloc(name_len + 1)
if mod_buf is None:
continue
string.strncpy(mod_buf, worklist + name_start, name_len)
mod_buf[name_len] = '\0'
# 检查是否已处理
already: int = 0
if HandlesImports.is_module_imported(processed, mod_buf) != 0:
already = 1
if already == 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)
if found_sha1 is not None:
# 加入 reachable_set
reachable_count = _AddSha1ToSet(reachable_set, reachable_count, found_sha1)
# 读取 .deps.txt 追加到 worklist
deps_path: str = _sliced_path(temp_dir, td_len_r, found_sha1, "deps.txt")
if deps_path is not None:
df: fileio.File | t.CPtr = fileio.File(deps_path, fileio.MODE.R)
if not df.closed:
deps_buf: bytes = stdlib.malloc(2048)
if deps_buf is not None:
dbr: t.CInt64T = df.read_all(deps_buf, 2048)
df.close()
if dbr > 0:
if dbr < 2048:
deps_buf[dbr] = '\0'
else:
deps_buf[2047] = '\0'
# 追加到 worklist嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug
dl: t.CSizeT = string.strlen(deps_buf)
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 = wl_len + 1
worklist[wl_len] = '\0'
stdlib.free(deps_buf)
else:
df.close()
stdlib.free(deps_path)
stdlib.free(mod_buf)
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
stdlib.free(worklist)
stdlib.free(processed)
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
# 仅加载实际使用的依赖 stub而非全部扫描
# _sha1_map.txt 仅作为人类可读输出,程序内部不读取。
# ============================================================
def BuildCombinedIR(temp_dir: str, local_sha1: str,
out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT:
"""组合本地 stub + 依赖 stubs + 本地 text → out_buf按需加载"""
if temp_dir is None or local_sha1 is None or out_buf is None or out_size == 0:
return 0
td_len: t.CSizeT = string.strlen(temp_dir)
out_buf[0] = '\0'
out_pos: t.CSizeT = 0
# 1. 读取并追加本地 stub.ll含 header
stub_path: str = _sliced_path(temp_dir, td_len, local_sha1, "stub.ll")
if stub_path is None:
return 0
sf: fileio.File | t.CPtr = fileio.File(stub_path, fileio.MODE.R)
if sf.closed:
stdlib.free(stub_path)
return 0
stub_content: bytes = stdlib.malloc(STUB_READ_BUF_SIZE)
if stub_content is None:
sf.close()
stdlib.free(stub_path)
return 0
stub_br: t.CInt64T = sf.read_all(stub_content, STUB_READ_BUF_SIZE)
sf.close()
stdlib.free(stub_path)
if stub_br <= 0:
stdlib.free(stub_content)
return 0
if stub_br < STUB_READ_BUF_SIZE:
stub_content[stub_br] = '\0'
else:
stub_content[STUB_READ_BUF_SIZE - 1] = '\0'
slen: t.CSizeT = stub_br
# 修复本地 stub.ll 中字符串常量的非法 IR
# llvmlite OUTPUT_STUB 模式输出 "= external unnamed_addr constant" 的非法形式。
# 简单方案:搜索 "= external unnamed_addr constant" 并将 external 替换为 8 个空格。
fix_pos: t.CSizeT = 0
while fix_pos + 30 < slen:
ext_ptr: t.CPtr = string.strstr(stub_content + fix_pos, "= external unnamed_addr constant")
if ext_ptr is None:
break
# ext_ptr 指向 "= external ..." 中的 '='
# 'external' 开始于 ext_ptr + 2
ext_off: t.CSizeT = t.CSizeT(t.CUInt64T(ext_ptr) - t.CUInt64T(stub_content)) + 2
if ext_off + 8 <= slen:
stub_content[ext_off] = ' '
stub_content[ext_off + 1] = ' '
stub_content[ext_off + 2] = ' '
stub_content[ext_off + 3] = ' '
stub_content[ext_off + 4] = ' '
stub_content[ext_off + 5] = ' '
stub_content[ext_off + 6] = ' '
stub_content[ext_off + 7] = ' '
fix_pos = ext_off + 8
# 直接复制修复后的 stub 内容
if out_pos + slen + 2 < out_size:
string.strcpy(out_buf + out_pos, stub_content)
out_pos += slen
if out_buf[out_pos - 1] != '\n':
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
stdlib.free(stub_content)
# 2. 按需加载依赖 stubs根据 deps.txt 过滤,而非扫描全部)
# 先加载依赖 stub含 type 定义),确保 type 定义在本地 text.ll 的 define 块之前
# 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
if sha1_arr is not None and mod_arr is not None:
inc_count = _BuildIncludesSha1Map(temp_dir, td_len, sha1_arr, mod_arr)
# 2b. 读取 deps.txt 获取导入模块名集合
deps_buf: str = None
deps_loaded: int = 0
deps_path: str = _sliced_path(temp_dir, td_len, local_sha1, "deps.txt")
if deps_path is not None:
df_deps: fileio.File | t.CPtr = fileio.File(deps_path, fileio.MODE.R)
if not df_deps.closed:
deps_buf = stdlib.malloc(4096)
if deps_buf is not None:
deps_br: t.CInt64T = df_deps.read_all(deps_buf, 4096)
if deps_br > 0:
if deps_br < 4096:
deps_buf[deps_br] = '\0'
else:
deps_buf[4095] = '\0'
deps_loaded = 1
df_deps.close()
stdlib.free(deps_path)
# 2c. 递归扫描 temp_dir 中所有 .stub.ll按需加载
all_sha1_arr: str = stdlib.malloc(MAX_INCLUDES_SHA1 * 17)
all_count: int = 0
if all_sha1_arr is not None:
string.memset(all_sha1_arr, 0, MAX_INCLUDES_SHA1 * 17)
all_count = _collect_stub_sha1s(temp_dir, td_len, Config.Sha1SliceLevel, all_sha1_arr, 0, MAX_INCLUDES_SHA1)
if all_count > 0:
dep_buf: bytes = stdlib.malloc(STUB_READ_BUF_SIZE)
if dep_buf is not None:
si: int = 0
while si < all_count:
dep_sha1: str = all_sha1_arr + t.CSizeT(si) * 17
if string.strcmp(dep_sha1, local_sha1) != 0:
should_load: int = 0
# 检查是否在 includes 映射中
is_include: int = 0
if inc_count > 0:
for ii in range(inc_count):
idx_ii: t.CSizeT = t.CSizeT(ii) * 17
if string.strcmp(sha1_arr + idx_ii, dep_sha1) == 0:
is_include = 1
if deps_loaded != 0:
idx_mi: t.CSizeT = t.CSizeT(ii) * 64
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用户文件总是加载
should_load = 1
if should_load != 0:
out_pos = _LoadAndAppendStub(temp_dir, td_len, dep_sha1, dep_buf, out_buf, out_size, out_pos)
out_pos = _LoadAndAppendTextDeclares(temp_dir, td_len, dep_sha1, dep_buf, out_buf, out_size, out_pos)
si += 1
stdlib.free(dep_buf)
stdlib.free(all_sha1_arr)
# 2d. 验证 deps.txt 中所有依赖模块的 stub 文件都存在fail-fast
# 避免到 llc 才报 undefined value 错误。
# 仅验证在 includes 映射中找到的模块t/c 等内部库无 SHA1 映射,自动跳过)。
if deps_loaded != 0 and inc_count > 0:
dep_total: t.CSizeT = string.strlen(deps_buf)
dp: t.CSizeT = 0
while dp < dep_total:
# 跳过前导空格
while dp < dep_total:
if deps_buf[dp] != ' ':
break
dp += 1
if dp >= dep_total:
break
# 提取模块名(到下一个空格或末尾)
ms: t.CSizeT = dp
while dp < dep_total:
if deps_buf[dp] == ' ':
break
dp += 1
ml: t.CSizeT = dp - ms
if ml == 0:
continue
# 复制模块名到临时缓冲区并 null 终止
mod_nm: str = stdlib.malloc(ml + 1)
if mod_nm is None:
continue
string.strncpy(mod_nm, deps_buf + ms, ml)
mod_nm[ml] = '\0'
# 在 includes 映射中查找模块对应的 SHA1
found_s: str = None
for mi2 in range(inc_count):
idx_m2: t.CSizeT = t.CSizeT(mi2) * 64
if string.strcmp(mod_arr + idx_m2, mod_nm) == 0:
idx_s2: t.CSizeT = t.CSizeT(mi2) * 17
found_s = sha1_arr + idx_s2
break
if found_s is not None:
# 检查 stub 文件是否存在
chk_path: str = _sliced_path(temp_dir, td_len, found_s, "stub.ll")
if chk_path is not None:
chk_f: fileio.File | t.CPtr = fileio.File(chk_path, fileio.MODE.R)
if chk_f.closed:
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)
stdlib.free(mod_nm)
# 3. 读取并追加本地 text.ll行级去重跳过 stub.ll 中已存在的定义)
text_path: str = _sliced_path(temp_dir, td_len, local_sha1, "text.ll")
if text_path is None:
return out_pos
tf: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R)
if tf.closed:
stdlib.free(text_path)
# text.ll 不存在的情况,继续
else:
text_content: bytes = stdlib.malloc(STUB_READ_BUF_SIZE)
if text_content is None:
tf.close()
stdlib.free(text_path)
else:
text_br: t.CInt64T = tf.read_all(text_content, STUB_READ_BUF_SIZE)
tf.close()
stdlib.free(text_path)
if text_br > 0:
if text_br < STUB_READ_BUF_SIZE:
text_content[text_br] = '\0'
else:
text_content[STUB_READ_BUF_SIZE - 1] = '\0'
# 行级去重追加(与 _LoadAndAppendStub 相同逻辑)
tx_pos: t.CSizeT = 0
tx_skip: int = 1
while tx_pos < text_br:
tx_ls: t.CSizeT = tx_pos
while tx_pos < text_br:
if text_content[tx_pos] == '\n':
break
tx_pos += 1
tx_ll: t.CSizeT = tx_pos - tx_ls
if tx_pos < text_br:
tx_pos += 1
if tx_skip != 0:
if tx_ll == 0:
continue
tx_c0: int = text_content[tx_ls]
if tx_c0 == ';':
continue
if tx_c0 == 't' and tx_ll >= 6:
if string.strncmp(text_content + tx_ls, "target", 6) == 0:
continue
if tx_c0 == 's' and tx_ll >= 15:
if string.strncmp(text_content + tx_ls, "source_filename", 15) == 0:
continue
tx_skip = 0
# 去重declare用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句)
if tx_ll >= 8 and string.strncmp(text_content + tx_ls, "declare ", 8) == 0:
tx_at: t.CSizeT = tx_ls + 8
while tx_at < tx_ls + tx_ll:
if text_content[tx_at] == '@':
break
tx_at += 1
if tx_at < tx_ls + tx_ll:
tx_lp: t.CSizeT = tx_at + 1
while tx_lp < tx_ls + tx_ll:
if text_content[tx_lp] == '(':
break
tx_lp += 1
if tx_lp < tx_ls + tx_ll:
tx_sv: t.CChar = text_content[tx_lp + 1]
text_content[tx_lp + 1] = '\0'
if _IsFuncDeclaredOrDefined(out_buf, text_content + tx_at) != 0:
text_content[tx_lp + 1] = tx_sv
continue
text_content[tx_lp + 1] = tx_sv
# 去重define注释掉 out_buf 中已有的 declare避免 declare/define 冲突)
if tx_ll >= 7 and string.strncmp(text_content + tx_ls, "define ", 7) == 0:
tx_at_d: t.CSizeT = tx_ls + 7
while tx_at_d < tx_ls + tx_ll:
if text_content[tx_at_d] == '@':
break
tx_at_d += 1
if tx_at_d < tx_ls + tx_ll:
tx_lp_d: t.CSizeT = tx_at_d + 1
while tx_lp_d < tx_ls + tx_ll:
if text_content[tx_lp_d] == '(':
break
tx_lp_d += 1
if tx_lp_d < tx_ls + tx_ll:
tx_sv_d: t.CChar = text_content[tx_lp_d + 1]
text_content[tx_lp_d + 1] = '\0'
_CommentOutDeclareInBuf(out_buf, text_content + tx_at_d)
text_content[tx_lp_d + 1] = tx_sv_d
# 处理:@global = ...
# stub 中可能是 external 声明或完整定义,以 text 中的定义为准
# 找到 stub 中同名全局变量行并注释掉,然后追加 text 中的
if tx_ll > 0 and text_content[tx_ls] == '@':
tx_eq: t.CSizeT = tx_ls + 1
while tx_eq < tx_ls + tx_ll:
if text_content[tx_eq] == '=':
break
tx_eq += 1
if tx_eq < tx_ls + tx_ll:
tx_sv2: t.CChar = text_content[tx_eq + 1]
text_content[tx_eq + 1] = '\0'
_CommentOutGlobalInBuf(out_buf, text_content + tx_ls)
text_content[tx_eq + 1] = tx_sv2
# 去重:%type = ...
if tx_ll > 0 and text_content[tx_ls] == '%':
tx_eq2: t.CSizeT = tx_ls + 1
while tx_eq2 < tx_ls + tx_ll:
if text_content[tx_eq2] == '=':
break
tx_eq2 += 1
if tx_eq2 < tx_ls + tx_ll:
tx_sv3: t.CChar = text_content[tx_eq2 + 1]
text_content[tx_eq2 + 1] = '\0'
if string.strstr(out_buf, text_content + tx_ls) is not None:
text_content[tx_eq2 + 1] = tx_sv3
continue
text_content[tx_eq2 + 1] = tx_sv3
# 追加
if out_pos + tx_ll + 2 < out_size:
string.strncpy(out_buf + out_pos, text_content + tx_ls, tx_ll)
out_pos += tx_ll
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
stdlib.free(text_content)
if deps_buf is not None:
stdlib.free(deps_buf)
if sha1_arr is not None:
stdlib.free(sha1_arr)
if mod_arr is not None:
stdlib.free(mod_arr)
return out_pos