Files
ViperC/App/lib/Stub/StubMerger/StubMergerBuild.vp
T

1230 lines
61 KiB
Plaintext
Raw 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 lib.core.Gen.GenApi as GenApi
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.HandlesTranslator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerBuild - StubMerger 拆分(阶段 E):StubMergerBuild
#
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
# StubMerger._xxx 访问(main.vp 先例)。
# ============================================================
# ============================================================
# _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 = StubMergerCollect._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)
# 依赖 stub 缺失:includes 映射内的模块缺失属编译错误(fail-fast),
# 非映射模块(如 t/c 内部库、用户文件)按设计跳过
if StubMergerStore.LookupSha1RelPath(dep_sha1) is not None:
fb_ls: str = VLogger.fmt_buf()
if fb_ls is not None:
viperlib.snprintf(fb_ls, 1024, "依赖 stub 缺失: %s", dep_sha1)
VLogger.error(fb_ls, "StubMerger")
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:
# 依赖 stub 为空:纯声明/纯常量模块(如 stdio.vp 全 pass、CDefine 模块)
# 无可声明符号,stub.ll 为空属合法形态,静默跳过
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 StubMergerIR._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 = StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + line_start)
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
has_full: int = StubMergerIR._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 提取 declare(define 转 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 = StubMergerCollect._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)
# 依赖 text 缺失:includes 映射内的模块缺失属编译错误(fail-fast),
# 非映射模块(如 t/c 内部库、用户文件)按设计跳过
if StubMergerStore.LookupSha1RelPath(dep_sha1) is not None:
fb_lt: str = VLogger.fmt_buf()
if fb_lt is not None:
viperlib.snprintf(fb_lt, 1024, "依赖 text 缺失: %s", dep_sha1)
VLogger.error(fb_lt, "StubMerger")
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:
# 依赖 text 为空:纯 stub 模块(如 stdio.vp 全 pass 声明)无函数体,
# text.ll 为空属合法形态,无可提取 declare,静默跳过
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 StubMergerIR._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)
# 泛型特化方法 define 是 "define linkonce_odr ..."(FunctionPrint
# 对名含 '[' 的函数输出 linkonce_odr):若只跳过 "define " 会拼出
# "declare linkonce_odr ..." → llc 报 "invalid linkage for function
# declaration"(LLVM 只允许 define 用 linkonce_odr,declare 必须
# external)。对齐 TPC Phase2Translator 的 decl_line 剥 linkage 逻辑,
# 循环跳过所有 linkage 词。
c_start_lt: t.CSizeT = ls + 7
skipped_lt: int = 1
while skipped_lt != 0:
skipped_lt = 0
if c_start_lt + 13 <= decl_end and \
string.strncmp(dep_buf + c_start_lt, "linkonce_odr ", 13) == 0:
c_start_lt += 13
skipped_lt = 1
elif c_start_lt + 10 <= decl_end and \
string.strncmp(dep_buf + c_start_lt, "weak_odr ", 9) == 0:
c_start_lt += 9
skipped_lt = 1
elif c_start_lt + 10 <= decl_end and \
string.strncmp(dep_buf + c_start_lt, "linkonce ", 9) == 0:
c_start_lt += 9
skipped_lt = 1
elif c_start_lt + 6 <= decl_end and \
string.strncmp(dep_buf + c_start_lt, "weak ", 5) == 0:
c_start_lt += 5
skipped_lt = 1
elif c_start_lt + 10 <= decl_end and \
string.strncmp(dep_buf + c_start_lt, "internal ", 9) == 0:
c_start_lt += 9
skipped_lt = 1
elif c_start_lt + 9 <= decl_end and \
string.strncmp(dep_buf + c_start_lt, "private ", 8) == 0:
c_start_lt += 8
skipped_lt = 1
copy_len_lt: t.CSizeT = decl_end - c_start_lt
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 + c_start_lt, 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 声明(如果有)
StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls)
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
has_full_td: int = StubMergerIR._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 中的全局变量带初值定义(如 @StubMerger._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'
# 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义)
# 时跳过:跨模块全局(如 _op_head、MemBuddy.vtable)只需一份
# 强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致
# ld.lld duplicate symbol(NewAstTest _op_head/MemBuddy.vtable 根因)。
# 仅当依赖方完全没有该全局(既无 external 也无定义)时才需要补充。
if StubMergerIR._HasGlobalInBuf(out_buf, dep_buf + ls) == 0:
# 注释掉 out_buf 中的同名行(external 声明或旧定义)
StubMergerIR._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'
else:
dep_buf[eq_gv] = saved_gv
continue
# 跳过其他行
return out_pos
# ============================================================
# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需翻译)
#
# 从 source_dir 的源文件开始,解析 import 语句,递归收集可达的
# includes 文件 SHA1。Phase 1b 只翻译可达集合中的文件,避免
# 翻译不需要的 includes(如 Test 不依赖 llvmlite,则不翻译)。
#
# 算法(工作列表):
# 1. 扫描 source_dir 下的 .vp/.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: str,
app_deps: str = None) -> int:
"""构建可达 SHA1 集合(依赖图按需翻译)
app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤
"""
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 = StubMergerIR._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: str = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count)
VLogger.debug(fb, "Reachable")
# 工作列表(空格分隔的模块名)
worklist: bytes = stdlib.malloc(REACH_WORKLIST_BUF)
if worklist is None:
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
worklist[0] = '\0'
wl_len: t.CSizeT = 0
reachable_count: int = 0
# 2. 如果 app_deps 非空,直接使用它作为初始 worklist(跳过自己的非递归扫描)
# 否则,回退到自己的非递归扫描(source_dir/*.py,只扫描顶层)
if app_deps is not None and app_deps[0] != '\0':
ad_len: t.CSizeT = string.strlen(app_deps)
if ad_len > 0 and ad_len < REACH_WORKLIST_BUF:
string.strcpy(worklist, app_deps)
wl_len = ad_len
else:
VLogger.error("app_deps 过长,无法构建可达集合", "StubMerger")
else:
# app_deps 缺失:Phase1 场景(未翻译 App 源文件)为设计性回退,
# 显式 warning 暴露回退,不再静默。
# 原实现仅非递归扫描 source_dir 顶层("%s/*"),漏掉 lib/ 等子目录的
# App 文件 → 其依赖(如 lib/Projectrans/Config.vp 的 json)不进入
# worklist → json 不可达 → Phase1 不 stub 不翻译 → Phase2 Phase A
# 翻译 Config.vp 时 root.is_object() 跨模块调用无 stub 可查 → 静默
# translate_value 返回 None → "if 条件表达式翻译失败"(追加82 根因)。
# 改为递归扫描(复用 IncludesScanner.scan_includes),覆盖全部子目录。
VLogger.warning("app_deps 缺失,递归扫描 source_dir", "Reachable")
scan_res_r: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, source_dir)
if scan_res_r is not None:
es_r: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
for e_i_r in range(scan_res_r.Count):
e_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(
t.CVoid(t.CUInt64T(scan_res_r.Entries) + t.CUInt64T(e_i_r) * es_r, t.CPtr))
if e_r is None or e_r.Path is None:
continue
sbuf: bytes = Utils.read_file_to_buf(e_r.Path, SRC_BUF_SIZE_R)
if sbuf is None:
continue
# 解析 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
# 源文件包名:用 RelPath(相对 source_dir,支持子目录),
# 保证 from .xxx import 相对导入解析正确
tr.CurrentPackage = GenApi.compute_package_from_relpath(mb, e_r.RelPath)
GenApi.reset_visible_structs(mb, 0)
tr.translate(tree)
# 容器使用检测:App 源文件用 list/dict(裸名注解/构造/[]
# /{} 字面量)时注入 _list/_dict 依赖(与原实现一致)
tr._inject_auto_container_visibility(mb, tree)
# 获取 _imported_modules
if tr._imported_modules is not None:
# 逐模块去重追加(同 app_deps 拼接,防 worklist 膨胀溢出)
im_len: t.CSizeT = string.strlen(tr._imported_modules)
im_pos: t.CSizeT = 0
while im_pos < im_len:
im_start: t.CSizeT = im_pos
while im_pos < im_len and tr._imported_modules[im_pos] != ' ':
im_pos += 1
im_mod_len: t.CSizeT = im_pos - im_start
if im_pos < im_len:
im_pos += 1
if im_mod_len == 0:
continue
im_buf: bytes = stdlib.malloc(im_mod_len + 1)
if im_buf is None:
continue
string.strncpy(im_buf, tr._imported_modules + im_start, im_mod_len)
im_buf[im_mod_len] = '\0'
if wl_len > 0 and GenApi.is_module_imported(worklist, im_buf) != 0:
stdlib.free(im_buf)
continue
if wl_len + im_mod_len + 2 < REACH_WORKLIST_BUF:
if wl_len > 0:
worklist[wl_len] = ' '
wl_len += 1
string.strcpy(worklist + wl_len, im_buf)
wl_len += im_mod_len
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
stdlib.free(im_buf)
# 计算 App 源文件 SHA1,填充依赖到内存存储器
# worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖
if e_r.Sha1 is not None:
StubMergerStore.PopulateIncludesDeps(e_r.Sha1, tr._imported_modules)
# 释放 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)
# 去掉末尾多余空格
if wl_len > 0 and worklist[wl_len - 1] == ' ':
worklist[wl_len - 1] = '\0'
wl_len -= 1
fb: str = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "源文件直接依赖: '%s'", worklist)
VLogger.debug(fb, "Reachable")
# 2.5. 预加入基础模块(builtins)
#
# builtins(内置函数模块:ord/chr 等)始终纳入编译链,调用时自动关联。
# _list/_dict 不再无条件预加入:Phase A 翻译 App 源文件时
# _inject_auto_container_visibility 已按需检测 list/dict 使用并注入
# _imported_modules(含 []/{} 字面量),json 由 _dict 的真实 import 链带入。
if GenApi.is_module_imported(worklist, "builtins") == 0:
bsha1_b: str = StubMergerIR._FindSha1ByModName(sha1_arr, mod_arr, map_count, "builtins")
if bsha1_b is not None:
if wl_len + 9 < REACH_WORKLIST_BUF:
if wl_len > 0:
worklist[wl_len] = ' '
wl_len += 1
string.strcpy(worklist + wl_len, "builtins")
wl_len += 8
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
fb: str = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist)
VLogger.debug(fb, "Reachable")
# 3. 工作列表算法:递归收集可达 SHA1
processed: bytes = stdlib.malloc(REACH_WORKLIST_BUF)
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 GenApi.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 < REACH_WORKLIST_BUF:
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'
else:
VLogger.error("依赖 worklist 溢出", "StubMerger")
# 查找 SHA1
found_sha1: str = StubMergerIR._FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf)
if found_sha1 is not None:
# App 源文件(Sha1Store 中 IncDir 为空,AppendToSha1MapStore 规则)
# 不加入 reachable_set:Phase B 已编译其 .obj,若加入会导致 Phase B+
# 把 App 文件重复编译进 includes.binary,Phase C 链接时与 Phase B 的
# App obj 重复符号(SysLibTest ld.lld duplicate symbol: StubMerger._mbuddy 根因)。
# 依赖仍继续展开(App 文件是依赖图中间节点,其 includes 依赖必须可达)。
is_app_r: int = 0
if StubMerger._sha1_store_incdir is not None:
for ai_r in range(StubMerger._sha1_store_count):
asi_r: t.CSizeT = t.CSizeT(ai_r) * 17
if string.strcmp(StubMerger._sha1_store_arr + asi_r, found_sha1) == 0:
aii_r: t.CSizeT = t.CSizeT(ai_r) * MAX_INC_DIR_LEN
if StubMerger._sha1_store_incdir[aii_r] == '\0':
is_app_r = 1
break
if is_app_r == 0:
reachable_count = StubMergerIR._AddSha1ToSet(reachable_set, reachable_count, found_sha1)
# 从内存查找依赖(不读 deps.txt 过程文件)
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存
deps_str: str = StubMergerStore.LookupIncludesDeps(found_sha1)
if deps_str is not None:
dl: t.CSizeT = string.strlen(deps_str)
if dl > 0:
# 逐模块追加 + 去重:worklist 初始已含全部 App 直接依赖,
# deps_str 中的模块若已在 worklist/processed 不重复追加,
# 否则 BFS 沿共享依赖(builtins/stdlib 等)反复累积到
# 64KB 溢出("依赖 worklist 溢出"根因)。
dpos: t.CSizeT = 0
while dpos < dl:
dstart: t.CSizeT = dpos
while dpos < dl and deps_str[dpos] != ' ':
dpos += 1
dlen: t.CSizeT = dpos - dstart
if dpos < dl:
dpos += 1
if dlen == 0:
continue
dmod: bytes = stdlib.malloc(dlen + 1)
if dmod is None:
continue
string.strncpy(dmod, deps_str + dstart, dlen)
dmod[dlen] = '\0'
in_wl: int = 0
if GenApi.is_module_imported(worklist, dmod) != 0:
in_wl = 1
if in_wl == 0 and processed is not None:
if GenApi.is_module_imported(processed, dmod) != 0:
in_wl = 1
if in_wl == 0:
if wl_len + dlen + 2 < REACH_WORKLIST_BUF:
if wl_len > 0 and worklist[wl_len - 1] != ' ':
worklist[wl_len] = ' '
wl_len = wl_len + 1
string.strcpy(worklist + wl_len, dmod)
wl_len = wl_len + dlen
worklist[wl_len] = ' '
wl_len = wl_len + 1
worklist[wl_len] = '\0'
else:
VLogger.error("依赖 worklist 溢出", "StubMerger")
stdlib.free(dmod)
# deps_str 为空字符串视为无依赖(合法情况,如叶子模块)
else:
# 依赖未在内存中找到
# 按需过滤: 检查是否是 App 源文件(source_dir 下的 .vp/.py 文件)
# App 源文件不需要加入 reachable_set(只用于 includes 过滤),跳过
sd_len_ff: t.CSizeT = string.strlen(source_dir)
mb_len_ff: t.CSizeT = string.strlen(mod_buf)
app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6)
is_app_src: int = 0
if app_mod_path is not None:
# 优先尝试 .vp,向后兼容 .py
viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.vp", source_dir, mod_buf)
af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R)
if not af.closed:
is_app_src = 1
af.close()
else:
# .vp 不存在,回退 .py
viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.py", source_dir, mod_buf)
af = fileio.File(app_mod_path, fileio.MODE.R)
if not af.closed:
is_app_src = 1
af.close()
stdlib.free(app_mod_path)
if is_app_src == 0:
# fast-fail: includes 文件依赖未在内存中找到,Phase 1a-pre 未正确填充
fb_ff: str = VLogger.fmt_buf()
if fb_ff is not None:
viperlib.snprintf(fb_ff, 1024, "依赖未找到 fast-fail: 模块 %s(sha1=%s) 的依赖未在内存中注册", mod_buf, found_sha1)
VLogger.error(fb_ff, "Reachable")
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
stdlib.free(worklist)
stdlib.free(processed)
stdlib.free(mod_buf)
return -1
stdlib.free(mod_buf)
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
stdlib.free(worklist)
stdlib.free(processed)
fb: str = 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 = StubMergerCollect._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 = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len, sha1_arr, mod_arr)
# 2b. 读取 deps.txt 获取导入模块名集合
deps_buf: str = None
deps_loaded: int = 0
deps_path: str = StubMergerCollect._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
elif deps_br == 0:
# 空 deps.txt:无 import 的包标记文件(如 lib/core/Gen/__init__.vp、
# lib/StubGen/__init__.vp 纯注释)合法——deps_loaded 置 1 视为
# "已加载且无依赖",避免下方"deps.txt 缺失"误报(追加85 根因)。
# deps_buf 是 malloc 未初始化内存,必须显式置空串,
# 否则后续 strlen(deps_buf) 读到垃圾长度遍历越界。
deps_buf[0] = '\0'
deps_loaded = 1
df_deps.close()
stdlib.free(deps_path)
# deps.txt 缺失但存在 includes 映射:依赖无法按需过滤,fail-fast 终止
if deps_loaded == 0 and inc_count > 0:
VLogger.error("依赖文件 deps.txt 缺失,无法合并 includes stub", "StubMerger")
# 2c. 递归扫描 temp_dir 中所有 .stub.ll,按需加载
# 全目录扫描结果按 temp_dir 缓存(Phase B 对每个源文件调用一次
# BuildCombinedIR,每次递归扫盘 O(N×S) 是 Phase B 慢的根因之一;
# 首次扫描后复用缓存,temp_dir 变化时自动重建)
cache_valid_sc: int = 0
if StubMerger._stub_scan_cache_arr is not None and StubMerger._stub_scan_cache_dir is not None:
if string.strcmp(StubMerger._stub_scan_cache_dir, temp_dir) == 0:
cache_valid_sc = 1
if cache_valid_sc == 0:
new_scan_arr: str = stdlib.malloc(MAX_INCLUDES_SHA1 * 17)
if new_scan_arr is not None:
string.memset(new_scan_arr, 0, MAX_INCLUDES_SHA1 * 17)
new_scan_count: int = StubMergerCollect._collect_stub_sha1s(
temp_dir, td_len, Config.Sha1SliceLevel, new_scan_arr, 0, MAX_INCLUDES_SHA1)
if new_scan_count > 0:
# 缓存目录键(stdlib 深拷贝,跨调用存活)
dir_buf_sc: str = stdlib.malloc(td_len + 1)
if dir_buf_sc is not None:
string.strcpy(dir_buf_sc, temp_dir)
if StubMerger._stub_scan_cache_arr is not None:
stdlib.free(StubMerger._stub_scan_cache_arr)
StubMerger._stub_scan_cache_arr = None
if StubMerger._stub_scan_cache_dir is not None:
stdlib.free(StubMerger._stub_scan_cache_dir)
StubMerger._stub_scan_cache_dir = None
StubMerger._stub_scan_cache_arr = new_scan_arr
StubMerger._stub_scan_cache_dir = dir_buf_sc
StubMerger._stub_scan_cache_count = new_scan_count
else:
stdlib.free(new_scan_arr)
else:
stdlib.free(new_scan_arr)
all_sha1_arr: str = StubMerger._stub_scan_cache_arr
all_count: int = StubMerger._stub_scan_cache_count
if all_sha1_arr is not None and 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:
si += 1
continue
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:
continue
is_include = 1
if deps_loaded != 0:
idx_mi: t.CSizeT = t.CSizeT(ii) * 64
dep_mod_name: str = mod_arr + idx_mi
if GenApi.is_module_imported(deps_buf, dep_mod_name) != 0:
should_load = 1
elif StubMergerIR._is_submodule_of_imported(deps_buf, dep_mod_name) != 0:
# 传递性依赖:deps.txt 只记录直接导入(如 ast),
# 但包的 __init__.vp 内部 from .lexer import Lexer
# 会让 IR 引用 ast.lexer 子模块类型。
# 检查 dep_mod_name 是否是已导入包的子模块
# (如 ast.lexer 是 ast 的子模块)
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)
# all_sha1_arr 是模块级缓存(StubMerger._stub_scan_cache_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 = StubMergerCollect._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: str = 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")
chk_f.close()
stdlib.free(chk_path)
stdlib.free(mod_nm)
# 3. 读取并追加本地 text.ll(行级去重,跳过 stub.ll 中已存在的定义)
text_path: str = StubMergerCollect._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:
# 本地 text.ll 缺失:合并 IR 无法生成定义块,fail-fast 终止
fb_tlp: str = VLogger.fmt_buf()
if fb_tlp is not None:
viperlib.snprintf(fb_tlp, 1024, "本地 text.ll 缺失: %s", text_path)
VLogger.error(fb_tlp, "StubMerger")
stdlib.free(text_path)
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 StubMergerIR._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'
StubMergerIR._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'
StubMergerIR._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
# ============================================================
# LoadDepVpiIntoModule - 加载依赖模块的 .vpi 文件,注入 declare 到当前模块
#
# 遍历 imported_modules(空格分隔的模块名),对每个模块:
# 1. 用 LookupSha1ByModName 获取 SHA1
# 2. 用 _sliced_path 构造 .vpi 文件路径
# 3. 调用 VpiReader.ParseVpiIntoModule 解析 .vpi 并注入 declare
#
# 在 _translate_module_level 中、Import 处理完成后、translate_children
# 之前调用,确保跨模块函数调用时 declare 已存在。
#
# Args:
# pool: 内存池
# mod: 当前 LLVM 模块
# imported_modules: 已导入模块名字符串(空格分隔)
# from_imports: from-import 映射字符串("name:module name:module" 格式)
# temp_dir: temp 目录路径
# trans: 翻译器对象
#
# Returns:
# 成功加载的 .vpi 文件数
# ============================================================
def LoadDepVpiIntoModule(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
imported_modules: str,
from_imports: str,
temp_dir: str,
trans: HandlesTranslator.Translator | t.CPtr) -> int:
"""加载依赖模块的 .vpi 文件,将函数声明注入到当前模块"""
if pool is None or mod is None or temp_dir is None or trans is None:
return 0
if imported_modules is None:
return 0
td_len: t.CSizeT = string.strlen(temp_dir)
total_len: t.CSizeT = string.strlen(imported_modules)
success_count: int = 0
# 遍历 imported_modules 中的每个模块名(空格分隔)
ci: t.CSizeT = 0
while ci < total_len:
# 跳过前导空格
while ci < total_len and imported_modules[ci] == ' ':
ci += 1
if ci >= total_len:
break
# 提取模块名(到下一个空格或字符串末尾)
word_start: t.CSizeT = ci
while ci < total_len and imported_modules[ci] != ' ':
ci += 1
word_len: t.CSizeT = ci - word_start
if word_len == 0:
continue
# 复制模块名到临时缓冲区并 null 终止
mod_nm_buf: str = stdlib.malloc(word_len + 1)
if mod_nm_buf is None:
continue
string.strncpy(mod_nm_buf, imported_modules + word_start, word_len)
mod_nm_buf[word_len] = '\0'
# 用 LookupSha1ByModName 查找模块的 SHA1
sha1_val: str = StubMergerStore.LookupSha1ByModName(mod_nm_buf)
# 如果精确查找失败,尝试包名回退(如 "ast" → "ast.__init__")
# LookupSha1ByModName 内部已处理包名回退,此处无需重复
if sha1_val is not None:
# 构造 .vpi 文件路径
vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1_val, "vpi")
if vpi_path is not None:
# 检查 .vpi 文件是否存在(可读)
vf: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R)
if not vf.closed:
vf.close()
# 调用 VpiReader 解析 .vpi 并注入 declare
# (错误上下文帧由调用方 HandlesTranslator 维护)
ret: int = VpiReader.ParseVpiIntoModule(pool, mod, vpi_path, trans)
if ret == 1:
success_count += 1
stdlib.free(vpi_path)
# sha1_val 指向内部存储,无需 free
stdlib.free(mod_nm_buf)
return success_count