Files
ViperC/App/lib/core/Gen/Call/CallGenBase.vp
T

1221 lines
62 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.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Translator as HT
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import hashtable
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import w32.win32file as win32file
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubGen.VpiReader as VpiReader
# ============================================================
# CallGenBase - CallGenMixin 拆分 1/9(最底层父类):模块符号查找 +
# 返回类型解析基础设施
# ============================================================
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
ASM_TEXT_BUF_SIZE: t.CDefine = 2048
ASM_CONST_BUF_SIZE: t.CDefine = 512
ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
# ============================================================
# CallGenBase - 最底层父类(字段 + 模块查找 + 返回类型解析)
# ============================================================
@t.NoVTable
class CallGenBase:
"""调用分派最底层父类(字段 + 模块符号查找 + 返回类型解析基础设施)"""
# --- c.cexport 注册表(原 _g_cexport_sha1_arr/_g_cexport_func_arr/_g_cexport_count) ---
Gen: rawptr[Generator.Generator]
CexportSha1Arr: bytes
CexportFuncArr: bytes
CexportCount: int
# --- LLVMIR 寄存器序号 ---
LLVMIRRegSeq: int
# --- temp 目录 ---
TempDir: str
TempDirLen: sizet
# --- 闭包名表(原 _g_closure_names/_g_closure_count) ---
ClosureNames: str
ClosureCount: int
# --- 函数指针签名表(原 _g_funcptr_names/_g_funcptr_rets/_g_funcptr_count) ---
FuncptrNames: str
FuncptrRets: uint64 | t.CPtr
FuncptrCount: int
# --- 全局函数注册表(原 _global_funcs/_global_func_count) ---
GlobalFuncs: rawptr[GenTypes.FuncEntry]
GlobalFuncCount: int
# --- 全局函数表"名 → 槽下标"索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) ---
# GlobalFuncs 只追加不重排 ⇒ 槽下标稳定;Built == GlobalFuncCount 才可信,否则回退线性扫描。
GlobalFuncNameIdx: sizet | t.CPtr
GlobalFuncNameIdxCap: int
GlobalFuncNameIdxBuilt: int
# --- 全局默认参数表(原 _global_defaults/_global_defaults_count) ---
GlobalDefaults: rawptr[GenTypes.FuncDefaultsEntry]
GlobalDefaultsCount: int
# --- 依赖模块 text.ll「方法返回类型文本」缓存(_LookupMethodRetTyFromTextLL 性能优化) ---
# key = define 的完整 mangled 函数名("{sha1}.{后缀}"),值只存文本(绝不存 LLVMType*,其分配在每文件 reset 失效的 pool 中);表与键/值均 PermPool 分配,命中后用调用方 pool 重建类型,建表失败即不缓存并回退原扫描。
MethodRetTyMap: rawptr[hashtable.HashTable]
def InitCallGen(self) -> int:
"""初始化调用 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
if self is None:
return 1
self.CexportSha1Arr = None
self.CexportFuncArr = None
self.CexportCount = 0
self.LLVMIRRegSeq = 0
self.TempDir = None
self.TempDirLen = 0
self.ClosureNames = None
self.ClosureCount = 0
self.FuncptrNames = None
self.FuncptrRets = None
self.FuncptrCount = 0
self.GlobalFuncs = None
self.GlobalFuncCount = 0
self.GlobalFuncNameIdx = None
self.GlobalFuncNameIdxCap = 0
self.GlobalFuncNameIdxBuilt = -1
self.GlobalDefaults = None
self.GlobalDefaultsCount = 0
self.MethodRetTyMap = None
return 0
# P4 簇 1+2 迁移方法:模块 SHA1 查询 / CExport 注册表 / c.* 系列 / 内联汇编 / LLVMIR
def _lookup_module_sha1(self, mod_name: str) -> str:
"""查找模块名对应的 SHA1,找不到返回 None
委托给 StubMerger.LookupSha1ByModName(精确匹配 + 包名回退)。
"""
return StubMergerStore.LookupSha1ByModName(mod_name)
def _lookup_module_sha1_suffix(self, mod_suffix: str) -> str:
"""后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "llvmlite.__types"
用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点),
去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。
委托给 StubMerger.LookupSha1ByModNameSuffix(精确 + 后缀 + 反向后缀)。
"""
return StubMergerStore.LookupSha1ByModNameSuffix(mod_suffix)
def _module_package_from_sha1(self, pool: rawptr[memhub.MemBuddy],
mod_sha1: str) -> str:
"""从 SHA1 存储器的 rel_path 计算模块所属包名(目录部分,'/' → '.')"""
if pool is None or mod_sha1 is None:
return None
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
count: int = StubMergerStore.GetSha1StoreCount()
if rel_arr_ptr is None or sha1_arr_ptr is None or count <= 0:
return None
for i in range(count):
sidx: sizet = sizet(i) * 17
if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0:
continue
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
rel: str = rel_arr_ptr + ridx
last_slash: str = string.strrchr(rel, 47) # 47 = ord('/')
if last_slash is None:
return None
pkg_len: sizet = sizet(uint64(last_slash) - uint64(rel))
if pkg_len == 0:
return None
pkg_buf: str = pool.alloc(pkg_len + 1)
if pkg_buf is None:
return None
for pi in range(pkg_len):
if rel[pi] == '/':
pkg_buf[pi] = '.'
else:
pkg_buf[pi] = rel[pi]
pkg_buf[pkg_len] = '\0'
return pkg_buf
return None
def _lookup_reexport_from_vpi(self, pool: rawptr[memhub.MemBuddy],
mod_sha1: str, func_name: str) -> str:
"""re-export 表未命中时,从模块 .vpi 按需解析 from-import 定位定义模块
includes 模块(如 llvmlite/__init__)的 stub/text 已存在时不会被按需
全量翻译(Phase2 3.5 仅在 stub/text 缺失时翻译),而 re-export 表只在
declare_only=0 的全量翻译中注册(HandleImportFromNames),因此表可能为空。
本函数解析该模块 .vpi 的 "from .X import ... func_name ..." 语句,
返回定义模块名(如 llvmlite/__init__ 的 .__builder → "llvmlite.__builder")。
"""
if pool is None or mod_sha1 is None or func_name is None:
return None
# star import 候选模块累积(空格分隔):from X import * 无法确定归属,收集全部候选由调用方逐个验证
star_cands: str = None
star_cands_len: sizet = 0
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
if temp_dir is None:
return None
td_len: sizet = string.strlen(temp_dir)
vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, mod_sha1, "vpi")
if vpi_path is None:
return None
READ_SZ: sizet = 1048576
buf: bytes = stdlib.malloc(READ_SZ)
if buf is None:
stdlib.free(vpi_path)
return None
vf: rawptr[fileio.File] = fileio.File(vpi_path, fileio.MODE.R)
stdlib.free(vpi_path)
if vf.closed:
stdlib.free(buf)
return None
nrd: LONG = vf.read_all(buf, READ_SZ)
vf.close()
if nrd <= 0:
stdlib.free(buf)
return None
if nrd < READ_SZ:
buf[nrd] = 0
name_len: sizet = string.strlen(func_name)
# 模块自含定义标志:vpi 中行首 "def {func_name}(" 命中且未被同名
# from-import 覆盖 → 该模块自身定义 func_name,不是 re-export 源。
has_own_def: int = 0
total: sizet = string.strlen(buf)
pos: sizet = 0
while pos < total:
ls: sizet = pos
while pos < total and buf[pos] != '\n':
pos += 1
ll: sizet = pos - ls
if pos < total:
pos += 1
# 自含定义检测(须在 from-import 行匹配之前,对所有行生效):模块级 def 行首无缩进,类内方法 def 有缩进不会误命中
if ll > 4 and buf[ls] == 'd' and buf[ls + 1] == 'e' \
and buf[ls + 2] == 'f' and buf[ls + 3] == ' ':
fstart_od: sizet = ls + 4
fend_od: sizet = fstart_od
while fend_od < ls + ll and buf[fend_od] != '(' and buf[fend_od] != ' ':
fend_od += 1
fl2_od: sizet = fend_od - fstart_od
if fl2_od == name_len:
fd_eq_od: int = 1
fdi_od: sizet = 0
while fdi_od < fl2_od:
if buf[fstart_od + fdi_od] != func_name[fdi_od]:
fd_eq_od = 0
break
fdi_od += 1
if fd_eq_od != 0:
has_own_def = 1
continue
# 行必须以 "from " 开头
if ll < 6:
continue
if buf[ls] != 'f' or buf[ls + 1] != 'r' or buf[ls + 2] != 'o' or buf[ls + 3] != 'm' or buf[ls + 4] != ' ':
continue
# 找 " import " 分隔符
imp_pos: sizet = ls + 5
while imp_pos + 7 <= ls + ll:
if buf[imp_pos] == ' ' and buf[imp_pos + 1] == 'i' and buf[imp_pos + 2] == 'm' and \
buf[imp_pos + 3] == 'p' and buf[imp_pos + 4] == 'o' and buf[imp_pos + 5] == 'r' and \
buf[imp_pos + 6] == 't' and buf[imp_pos + 7] == ' ':
break
imp_pos += 1
if imp_pos + 7 > ls + ll:
continue
mod_start: sizet = ls + 5
mod_end: sizet = imp_pos
# 名称列表从 "import " 之后(逗号分隔,含 "A as B" 取原名 A)
nm_start: sizet = imp_pos + 8
# star import 支持(from X import *):首个 star 行未必是定义模块,累积全部候选,扫描完由调用方按 vpi 返回注解逐个验证
if nm_start < ls + ll and buf[nm_start] == '*':
star_dot: int = 0
if buf[mod_start] == '.':
star_dot = 1
star_buf: str = pool.alloc(mod_end - mod_start + 1)
if star_buf is not None:
si2: sizet = 0
while mod_start + si2 < mod_end:
star_buf[si2] = buf[mod_start + si2]
si2 += 1
star_buf[si2] = '\0'
while star_buf[0] == '.':
star_buf = star_buf + 1
if string.strlen(star_buf) > 0:
if star_dot != 0:
spkg: str = self._module_package_from_sha1(pool, mod_sha1)
if spkg is not None:
srl: sizet = string.strlen(spkg) + string.strlen(star_buf) + 2
sres: str = pool.alloc(srl)
if sres is not None:
viperlib.snprintf(sres, srl, "%s.%s", spkg, star_buf)
star_buf = sres
# 累积候选(空格分隔)
sbl: sizet = string.strlen(star_buf)
new_cands: str = pool.alloc(star_cands_len + sbl + 2)
if new_cands is not None:
if star_cands is not None and star_cands_len > 0:
string.strcpy(new_cands, star_cands)
new_cands[star_cands_len] = ' '
string.strcpy(new_cands + star_cands_len + 1, star_buf)
star_cands_len += sbl + 1
else:
string.strcpy(new_cands, star_buf)
star_cands_len = sbl
star_cands = new_cands
continue
scan: sizet = nm_start
while scan < ls + ll:
while scan < ls + ll and (buf[scan] == ' ' or buf[scan] == ','):
scan += 1
if scan >= ls + ll:
break
n_end: sizet = scan
while n_end < ls + ll and buf[n_end] != ',' and buf[n_end] != ' ':
n_end += 1
cand_start: sizet = scan
cand_len: sizet = n_end - scan
as_pos: sizet = scan
while as_pos + 4 <= n_end:
if buf[as_pos] == ' ' and buf[as_pos + 1] == 'a' and buf[as_pos + 2] == 's' and buf[as_pos + 3] == ' ':
cand_start = as_pos + 4
cand_len = n_end - cand_start
break
as_pos += 1
if cand_len == name_len:
m_ok: int = 1
for ki in range(name_len):
if buf[cand_start + ki] != func_name[ki]:
m_ok = 0
break
if m_ok == 1:
# 命中:解析模块名(前导点 → 相对导入,补包前缀)
has_dot: int = 0
if buf[mod_start] == '.':
has_dot = 1
mod_buf: str = pool.alloc(mod_end - mod_start + 1)
if mod_buf is None:
stdlib.free(buf)
return None
mi: sizet = 0
while mod_start + mi < mod_end:
mod_buf[mi] = buf[mod_start + mi]
mi += 1
mod_buf[mi] = '\0'
while mod_buf[0] == '.':
mod_buf = mod_buf + 1
if string.strlen(mod_buf) == 0:
stdlib.free(buf)
return None
if has_dot != 0:
pkg: str = self._module_package_from_sha1(pool, mod_sha1)
if pkg is None:
stdlib.free(buf)
return None
rl: sizet = string.strlen(pkg) + string.strlen(mod_buf) + 2
res: str = pool.alloc(rl)
if res is None:
stdlib.free(buf)
return None
viperlib.snprintf(res, rl, "%s.%s", pkg, mod_buf)
stdlib.free(buf)
return res
stdlib.free(buf)
return mod_buf
scan = n_end
# 全部行扫描完毕:仅有 star import 时返回累积候选(调用方逐个验证定义模块);模块自含 def {func_name} 时本模块即定义者,返回 None 保持归属
if star_cands is not None:
stdlib.free(buf)
if has_own_def == 0:
return star_cands
return None
stdlib.free(buf)
return None
def register_cexport_func(self, sha1: str, func_name: str) -> int:
"""注册 CExport 函数到全局表(统一写入 Gen.CallGenBase 真源,跨子对象共享)"""
if self.Gen is None or sha1 is None or func_name is None:
return 0
if self.Gen.CallGenBase.CexportSha1Arr is None:
self.Gen.CallGenBase.CexportSha1Arr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 17)
self.Gen.CallGenBase.CexportFuncArr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 64)
if self.Gen.CallGenBase.CexportSha1Arr is None or self.Gen.CallGenBase.CexportFuncArr is None:
return 0
string.memset(self.Gen.CallGenBase.CexportSha1Arr, 0, _CEXPORT_MAX * 17)
string.memset(self.Gen.CallGenBase.CexportFuncArr, 0, _CEXPORT_MAX * 64)
if self.Gen.CallGenBase.CexportCount < _CEXPORT_MAX:
idx: sizet = sizet(self.Gen.CallGenBase.CexportCount) * 17
# 槽位定长 17/64 字节,strcpy 无界会越界写相邻槽(stdlib 堆区)
string.strncpy(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1, 16)
self.Gen.CallGenBase.CexportSha1Arr[idx + 16] = '\0'
idx2: sizet = sizet(self.Gen.CallGenBase.CexportCount) * 64
string.strncpy(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name, 63)
self.Gen.CallGenBase.CexportFuncArr[idx2 + 63] = '\0'
self.Gen.CallGenBase.CexportCount += 1
return 1
return 0
def is_cexport_func(self, sha1: str, func_name: str) -> int:
"""检查 (sha1, func_name) 是否是 CExport 函数(统一读 Gen.CallGenBase 真源)
注册表未命中时回退查 vpi 返回注解:跨模块编译存在时序差(如 apic.vp
先于 mm.vp 翻译时 mm 的 CExport 尚未 register_cexport_func → mm.malloc
误用 @1fdad1c76fb116bc.malloc 而非裸名 @malloc → ld.lld undefined
symbol: 1fdad1c76fb116bc.malloc)。
"""
if self.Gen is None or sha1 is None or func_name is None:
return 0
if self.Gen.CallGenBase.CexportSha1Arr is not None:
for i in range(self.Gen.CallGenBase.CexportCount):
idx: sizet = sizet(i) * 17
if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0:
idx2: sizet = sizet(i) * 64
if string.strcmp(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name) == 0:
return 1
return self._is_cexport_func_from_vpi(sha1, func_name)
def _is_cexport_func_from_vpi(self, sha1: str, func_name: str) -> int:
"""vpi 返回注解含 t.CExport → 定义端用裸名(跨模块调用无 sha1 头)
对齐 _is_state_func_from_vpi(State 标记)的 vpi 按需解析路径。
"""
if sha1 is None or func_name is None:
return 0
if self.Gen is None or self.Gen.CallGenBase.TempDir is None \
or self.Gen.CallGenBase.TempDirLen <= 0:
return 0
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
cexp_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
if cexp_annot is None:
return 0
if string.strstr(cexp_annot, "CExport") is not None:
return 1
return 0
def _is_state_func_from_vpi(self, pool: rawptr[memhub.MemBuddy],
sha1: str, func_name: str) -> int:
"""vpi 返回注解含 t.State → 外部 C 函数(定义端用裸名,跨模块调用无 sha1 头)
t.State(同 t.CExtern/t.CExport)是外部函数标记:定义端生成裸名
@PyList_Size(链接外部库 libpython),跨模块调用若生成 @{sha1}.PyList_Size
→ ld.lld undefined symbol(CPythonTest PyList_Size 根因)。CExport 走
is_cexport_func 注册表,State 函数未注册 → 按需解析 vpi 返回注解判断。
"""
if pool is None or sha1 is None or func_name is None:
return 0
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
state_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
if state_annot is None:
return 0
# 返回注解含 "State"(如 "sizet | t.State")→ 外部 C 函数
if string.strstr(state_annot, "State") is not None:
return 1
return 0
def _lookup_mod_sha1(self, pool: rawptr[memhub.MemBuddy],
from_imports: str, class_name: str,
allow_star_fallback: int = 1) -> str:
"""从 from_imports 查找类所属模块的 SHA1,返回 None=未找到
allow_star_fallback: 传 0 禁用 star import 回退(用于明确模块名/函数名查找)
"""
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(
from_imports, class_name, allow_star_fallback)
if mod_name_raw is None:
return None
return self._lookup_sha1_from_modname_raw(pool, mod_name_raw)
def _lookup_sha1_from_modname_raw(self, pool: rawptr[memhub.MemBuddy],
mod_name_raw: str) -> str:
"""从原始模块名查找 SHA1,返回 None=未找到"""
# 手动找到模块名结束位置(空格、null、或 ':' 别名分隔符),复制到新缓冲区
mod_len: sizet = 0
while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':':
mod_len += 1
mod_name_buf: str = pool.alloc(mod_len + 1)
if mod_name_buf is None:
return None
for mi in range(mod_len):
mod_name_buf[mi] = mod_name_raw[mi]
mod_name_buf[mod_len] = '\0'
# 去掉相对导入的前导点
base_mod: str = mod_name_buf
while base_mod[0] == '.':
base_mod = base_mod + 1
sha1: str = self._lookup_module_sha1(base_mod)
if sha1 is None:
sha1 = self._lookup_module_sha1_suffix(base_mod)
return sha1
def find_func_in_module(self, mod: rawptr[llvmlite.LLVMModule],
name: str,
owner_sha1: str = None) -> rawptr[llvmlite.Function]:
"""在模块的函数链表中按名称查找函数
查找策略:
1. 精确匹配(如 "strlen" 匹配 @strlen,有 t.CExport 的函数)
2. 后缀匹配(如 "atoi" 匹配 @"83210a72e829a086.atoi",无 t.CExport 的函数)
owner_sha1: 当前模块 SHA1。非 None 时在本模块 define 判定之前先做
"本模块同名函数"优先扫描(mangled 名以 "{owner_sha1}." 开头且恰为
"{owner_sha1}.{name}")。裸名调用应优先解析为当前模块自身函数——
其前向 declare 在函数体翻译时可能尚未激活(IsDeclared 仍为 1),
pass2 的 IsDeclared==0 判定会跳过它,pass3 又会按链表顺序误绑排在
FuncHead 前面的跨模块 stub declare(lexer.vp _advance 根因)。
使用 llvmlite.function_get_name/next 访问器绕过 stub 类型限制
(stub 类型字段不足且字段类型可能错误,直接字段访问会被静默跳过)。
"""
if mod is None or name is None:
return None
name_len: sizet = string.strlen(name)
# 热点优化:原 4 趟完整链表扫描合并为 2 趟——趟 1 精确匹配(命中即返回)+ 收集首个本模块同名 mangled 候选;趟 2 收集首个 IsDeclared==0 后缀命中与首个任意后缀命中,扫描后按原优先级返回(语义不变)
cur: rawptr[llvmlite.Function] = mod.FuncHead
mangle_hit: rawptr[llvmlite.Function] = None
while cur is not None:
cur_name: str = llvmlite.function_get_name(cur)
if cur_name is not None:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp(mangled 名以 sha1 十六进制打头,裸名多为字母/下划线)
if cur_name[0] == name[0] and string.strcmp(cur_name, name) == 0:
return cur
# 本模块同名候选:仅接受恰为 "{owner_sha1}.{name}" 的形态(不匹配 "{sha1}.Class.method");declare 与 define 同名同符号,是否 IsDeclared==0 均可返回
if owner_sha1 is not None and mangle_hit is None:
# 名字长度改取 Function.NameLen(创建时一次算好),免去对热链
# 每个节点重复 strlen(cur_name)(全程序 strlen 热点来源)。
cur_len_o: sizet = llvmlite.function_get_name_len(cur)
if cur_len_o == 17 + name_len:
if string.strncmp(cur_name, owner_sha1, 16) == 0 and cur_name[16] == '.':
suffix_start_o: sizet = cur_len_o - name_len
if cur_name[suffix_start_o - 1] == '.':
match_o: int = 1
for i in range(name_len):
if cur_name[suffix_start_o + i] != name[i]:
match_o = 0
break
if match_o == 1:
mangle_hit = cur
cur = llvmlite.function_get_next(cur)
if mangle_hit is not None:
return mangle_hit
# 后缀匹配(.name):不跳过 declare(方法 stub 也是 declare,由调用处判断 IsDeclared 决定用裸名或 sha1 前缀名)。本模块同名 define 必须优先于跨模块 stub declare,无同名 define 时回退 stub declare
declare_hit: rawptr[llvmlite.Function] = None
any_hit: rawptr[llvmlite.Function] = None
cur = mod.FuncHead
while cur is not None:
cur_name2: str = llvmlite.function_get_name(cur)
if cur_name2 is not None:
# 名字长度改取 Function.NameLen(创建时一次算好),免去热链上
# 每个节点重复 strlen(cur_name2);与 strlen(Name) 恒等,不改匹配语义。
cur_len: sizet = llvmlite.function_get_name_len(cur)
if cur_len > name_len + 1:
suffix_start: sizet = cur_len - name_len
if cur_name2[suffix_start - 1] == '.':
match: int = 1
for i in range(name_len):
if cur_name2[suffix_start + i] != name[i]:
match = 0
break
if match == 1:
# 首个任意后缀命中(原第三遍兜底)
if any_hit is None:
any_hit = cur
# 首个 IsDeclared==0 命中(原第二遍优先)
if cur.IsDeclared == 0 and declare_hit is None:
declare_hit = cur
cur = llvmlite.function_get_next(cur)
if declare_hit is not None:
return declare_hit
return any_hit
# _ensure_method_declare:found_func 为 None(stub 未注入或跨模块调用)时在当前模块创建 declare,避免 llc 报 undefined value
def _ensure_method_declare(self, pool: rawptr[memhub.MemBuddy],
mod: rawptr[llvmlite.LLVMModule],
call_name: str,
ret_ty: rawptr[llvmlite.LLVMType],
param_head: rawptr[llvmlite.Value],
param_count: int):
"""为跨模块方法调用创建 declare 声明
若模块中已存在同名 declare 且参数数量较少(例如 File.__init__ 的
stub 仅 3 个参数,而实际调用传入 4 个),则向现有 declare 追加缺失
参数,避免 llc 报 `argument invalid for parameter type` 之类的错误。
已存在的 define 不修改(其参数由定义决定)。
"""
if pool is None or mod is None or call_name is None:
return
# 链遍历哨兵(fail-fast):FuncHead/Params 的 Next 被越界写破坏成环时
# 无上限遍历会死循环(builtins.vp dump 卡死根因排查),超限即报错终止。
DECLARE_CHAIN_LIMIT: int = 100000
# 检查模块中是否已有同名函数(declare 或 define)
existing: rawptr[llvmlite.Function] = mod.FuncHead
existing_steps: int = 0
while existing is not None:
existing_steps += 1
if existing_steps > DECLARE_CHAIN_LIMIT:
stdio.printf("[CALLGEN] 链表异常: module funcs(call_name=%s,遍历超限,终止)\n", call_name)
stdlib.exit(1)
if existing.Name is not None:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
if existing.Name[0] == call_name[0] \
and string.strcmp(existing.Name, call_name) == 0:
# 已存在:仅当是 declare 且参数数量不足时追加缺失参数
if existing.IsDeclared == 1:
# 变长函数(*args,IsVarArg=1)不追加参数:多余实参由
# 变参机制(...)承接,追加有类型参数会导致调用侧把变参
# 强转为追加的类型(如 i8 → i64),%d/%x 等格式读错。
if existing.IsVarArg == 1:
return
# 遍历现有参数链表计数(避免依赖 GSList.Count 字段的类型推断)
cur_count: int = 0
cur_p: rawptr[llvmlite.Param] = llvmlite.function_get_param_head(existing)
while cur_p is not None:
cur_count += 1
if cur_count > DECLARE_CHAIN_LIMIT:
stdio.printf("[CALLGEN] 链表异常: func params(call_name=%s,参数链遍历超限,终止)\n", call_name)
stdlib.exit(1)
cur_p = cur_p.Next
# 只追加缺失的参数(cur_count < param_count 时)
if cur_count < param_count:
# 定位到 param_head 的第 cur_count 个节点开始追加
pnode2: rawptr[llvmlite.Value] = param_head
pi2: int = 0
# 跳过已有的 cur_count 个参数
while pnode2 is not None and pi2 < cur_count:
pnode2 = pnode2.Next
pi2 += 1
# 追加剩余参数
while pnode2 is not None and pi2 < param_count:
if pnode2.Ty is not None:
param2: rawptr[llvmlite.Param] = llvmlite.new_param(pool, pnode2.Ty, None)
if param2 is not None:
llvmlite.function_add_param(existing, param2)
pnode2 = pnode2.Next
pi2 += 1
return
existing = existing.Next
# 裸名 C 库函数(如 string.memset → @memset):call_name 无 sha1 前缀,无法提取定义侧模块查 vpi 参数注解,故按标准签名建 declare(与 CallGenLib._ensure_c_lib_declare 白名单一致)
if call_name is not None and string.strchr(call_name, 46) is None:
std_lib_fn: rawptr[llvmlite.Function] = self.Gen.CallGenLib._ensure_c_lib_declare(
pool, mod, call_name)
if std_lib_fn is not None:
return
# 创建函数声明
func: rawptr[llvmlite.Function] = llvmlite.new_function(pool, call_name, ret_ty)
if func is None:
return
func.IsDeclared = 1
# 定义侧参数类型优先:从 call_name 提取 {sha1}.{cls}.{method} 查 vpi 参数注解取定义侧类型,仅当实参与定义都是整数且定义更宽时采用定义宽度(指针/联合保持实参类型)。反方向(定义更窄)由 CallGenLib._coerce_args_to_def_params 在调用点先窄化实参
sha1_d: str = None
cls_d: str = None
meth_d: str = None
if call_name is not None:
# SHA1 前缀判定走唯一真源 GenApi.sha1_prefix_len,前缀长度取其返回值(不硬编码 17/16)
sha1_pfx_d: int = GenApi.sha1_prefix_len(call_name)
if sha1_pfx_d != 0:
pfx_len_d: sizet = sizet(sha1_pfx_d)
sha1_buf_d: bytes = stdlib.malloc(pfx_len_d)
if sha1_buf_d is not None:
string.strncpy(sha1_buf_d, call_name, pfx_len_d - 1)
sha1_buf_d[pfx_len_d - 1] = '\0'
sha1_d = sha1_buf_d
rest_d: str = call_name + pfx_len_d
# 取末个 '.':method 不含 '.',首个 '.' 会把含模块限定名的类型实参截断
dot2_d: str = string.strrchr(rest_d, '.')
if dot2_d is not None:
cls_len_d: sizet = uint64(dot2_d) - uint64(rest_d)
if cls_len_d > 0:
cls_buf_d: bytes = stdlib.malloc(cls_len_d + 1)
if cls_buf_d is not None:
string.strncpy(cls_buf_d, rest_d, cls_len_d)
cls_buf_d[cls_len_d] = '\0'
cls_d = cls_buf_d
meth_d = dot2_d + 1
else:
meth_d = rest_d
prm_annots_d: str = None
if sha1_d is not None and meth_d is not None:
prm_annots_d = self._vpi_method_param_annot(pool, sha1_d, cls_d, meth_d)
i32_ty_d: rawptr[llvmlite.LLVMType] = llvmlite.Int32(pool)
i8_ptr_d: rawptr[llvmlite.LLVMType] = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 添加参数(declare 中参数名无意义,设为 None 只输出类型)
pnode: rawptr[llvmlite.Value] = param_head
pi: int = 0
while pnode is not None and pi < param_count:
param_ty_d: rawptr[llvmlite.LLVMType] = pnode.Ty
if prm_annots_d is not None and prm_annots_d[0] != '\0' and pnode.Ty is not None:
seg_ty_d: rawptr[llvmlite.LLVMType] = VpiReader._resolve_vpi_param_annot_seg(
pool, sha1_d, cls_d, meth_d, pi, i32_ty_d, i8_ptr_d)
if seg_ty_d is not None:
match pnode.Ty:
case llvmlite.LLVMType.Int(bits_a):
match seg_ty_d:
case llvmlite.LLVMType.Int(bits_b):
if bits_b > bits_a:
param_ty_d = seg_ty_d
case _:
pass
case _:
pass
if param_ty_d is not None:
param: rawptr[llvmlite.Param] = llvmlite.new_param(pool, param_ty_d, None)
if param is not None:
llvmlite.function_add_param(func, param)
pnode = pnode.Next
pi += 1
if sha1_d is not None:
stdlib.free(sha1_d)
if cls_d is not None:
stdlib.free(cls_d)
llvmlite.module_add_function(mod, func)
# _lookup_method_ret_ty_via_vpi:按 模块SHA1 + 类名 + 方法名 查 vpi 返回注解缓存并解析为 LLVMType,未命中返回 None(不硬编码白名单,类型始终来自定义处)
# ============================================================
def _lookup_method_ret_ty_via_vpi(self, pool: rawptr[memhub.MemBuddy],
cls_sha1: str,
cls_name: str,
method_name: str) -> rawptr[llvmlite.LLVMType]:
"""从 vpi stub 返回注解缓存获取方法返回类型,未命中返回 None"""
if pool is None or cls_sha1 is None or method_name is None:
return None
# 函数内懒 import:VpiReader imports HandlesExprCall,顶层 import 会成环
i32_ty: rawptr[llvmlite.LLVMType] = llvmlite.Int32(pool)
i8_ptr_ty: rawptr[llvmlite.LLVMType] = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi
# 覆盖"模块自身翻译时 stub 尚未注入"(同模块方法调用,定义在后)的场景
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
annot: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, cls_name, method_name)
if annot is None:
return None
return VpiReader._resolve_vpi_ret_annot_str(pool, annot, i32_ty, i8_ptr_ty)
# _vpi_method_param_annot:按需加载并查询 vpi 参数注解串('@' 分隔,含首参 self 空串占位),供调用方补全 declare/call 缺失参数;未命中返回 None
def _vpi_method_param_annot(self, pool: rawptr[memhub.MemBuddy],
cls_sha1: str,
cls_name: str,
method_name: str) -> str:
"""按需加载并查询 vpi 参数注解串,未命中返回 None"""
if pool is None or cls_sha1 is None or cls_name is None or method_name is None:
return None
# 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_param_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
return VpiReader._lookup_vpi_param_annots(cls_sha1, cls_name, method_name)
# _vpi_resolve_param_annot_ty:解析 vpi 参数注解段为 LLVMType,失败返回 None(调用方以 i8* 兜底)
def _vpi_resolve_param_annot_ty(self, pool: rawptr[memhub.MemBuddy],
seg: str) -> rawptr[llvmlite.LLVMType]:
"""解析单个 vpi 参数注解段为 LLVMType,失败返回 None"""
if pool is None or seg is None:
return None
i32_ty: rawptr[llvmlite.LLVMType] = llvmlite.Int32(pool)
i8_ptr_ty: rawptr[llvmlite.LLVMType] = llvmlite.Ptr(pool, llvmlite.Int8(pool))
return VpiReader._resolve_vpi_ret_annot_str(pool, seg, i32_ty, i8_ptr_ty)
# _special_method_ret_ty:编译器内置特殊方法的固定返回类型——__before_init__/__init__ → void,__new__ → Ptr(StructTy),非特殊返回 None(vpi 无条目,固定即可避免 fail-fast 误报)
# ============================================================
def _special_method_ret_ty(self, pool: rawptr[memhub.MemBuddy],
class_name: str,
method_name: str) -> rawptr[llvmlite.LLVMType]:
"""编译器内置特殊方法的固定返回类型;非特殊返回 None"""
if pool is None or method_name is None:
return None
if string.strcmp(method_name, "__before_init__") == 0 or \
string.strcmp(method_name, "__init__") == 0:
return llvmlite.Void(pool)
if string.strcmp(method_name, "__new__") == 0:
if class_name is not None:
sp_struct_ty: rawptr[llvmlite.LLVMType] = self.Gen.StructGen.get_struct_type(class_name)
if sp_struct_ty is not None:
return llvmlite.Ptr(pool, sp_struct_ty)
return None
return None
# _fail_method_ret_ty:方法返回类型无法精确获取时 ERROR + 错误堆栈终止(node 为调用点 AST,用于定位),绝不静默默认 i32
# ============================================================
def _fail_method_ret_ty(self, method_name: str,
node: rawptr[ast.AST] = None) -> rawptr[llvmlite.LLVMType]:
"""方法返回类型无法精确获取时 fail-fast(ERROR + 错误堆栈)
返回类型来源(按序):当前模块函数 → text.ll define → vpi stub 返回注解缓存。
全部未命中说明方法定义缺失或 stub 损坏,立即终止暴露,绝不静默默认 i32。
"""
VLogger.error(
f"方法返回类型无法精确获取(函数/text.ll/vpi stub 均未命中): {method_name}",
"HandlesExprCall")
self.Gen.TypeGenErr.fatal_error(
node,
f"方法返回类型无法精确获取,默认 i32 会截断指针/i64: {method_name}",
"TypeError")
return None
# SetTempDir:设置 temp 目录路径(Phase A 翻译前调用,供读取依赖模块 text.ll)
# ============================================================
def SetTempDir(self, temp_dir: str) -> None:
"""设置 temp 目录路径(统一写入 Gen.CallGenBase 真源,跨子对象共享)"""
if self.Gen is None:
return
self.Gen.CallGenBase.TempDir = temp_dir
if temp_dir is not None:
self.Gen.CallGenBase.TempDirLen = string.strlen(temp_dir)
else:
self.Gen.CallGenBase.TempDirLen = 0
# _ParseLLVMRetTyStr:把 LLVM IR 返回类型字符串(define 与 @ 之间)转为 LLVMType——void / iN / double / float,含 * 者返回通用 Ptr(Int8);无法解析返回 None
# ============================================================
def _ParseLLVMRetTyStr(self, pool: rawptr[memhub.MemBuddy],
buf: bytes,
start: sizet,
end: sizet) -> rawptr[llvmlite.LLVMType]:
"""将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType"""
if pool is None or buf is None or start >= end:
return None
# 去除前导空白
s: sizet = start
while s < end:
c: int = buf[s]
if c != ' ' and c != '\t':
break
s += 1
# 去除尾部空白
e: sizet = end
while e > s:
c2: int = buf[e - 1]
if c2 != ' ' and c2 != '\t':
break
e -= 1
ty_len: sizet = e - s
if ty_len == 0:
return None
# struct 字面量 {T1, T2, ...}(tuple 打包返回类型)必须在 * 检查之前,否则内含 * 会被误判为 i8*(破坏 tuple 解包)
if s < e and buf[s] == '{':
th_st: rawptr[llvmlite.Function] = None
tt_st: rawptr[llvmlite.Function] = None
tc_st: int = 0
ps_st: sizet = s + 1
while ps_st < e and buf[ps_st] != '}':
while ps_st < e and (buf[ps_st] == ' ' or buf[ps_st] == ','):
ps_st += 1
if ps_st >= e or buf[ps_st] == '}':
break
es_st: sizet = ps_st
while ps_st < e and buf[ps_st] != ',' and buf[ps_st] != '}':
ps_st += 1
ee_st: sizet = ps_st
if ee_st > es_st:
et_st: rawptr[llvmlite.LLVMType] = self._ParseLLVMRetTyStr(
pool, buf, es_st, ee_st)
if et_st is None:
return None
en_st: rawptr[llvmlite.Function] = llvmlite.new_param_node(
pool, et_st)
if en_st is None:
return None
if th_st is None:
th_st = en_st
else:
llvmlite.paramnode_set_next(tt_st, en_st)
tt_st = en_st
tc_st += 1
if th_st is None or tc_st == 0:
return None
st_ty_st: rawptr[llvmlite.LLVMType] = llvmlite.Struct(pool, th_st, tc_st, None)
# 指针到 struct({i32, i8}*):返回 Ptr(Struct);纯 tuple 打包
# ({i8*, i16})无尾部 *,返回 Struct 本身。
if ps_st < e and buf[ps_st] == '*':
return llvmlite.Ptr(pool, st_ty_st)
return st_ty_st
# 检查是否含 * (指针类型) → 返回 i8* (通用指针)
# 包括 i8*, i32*, %"name"*, { i32, i8 }* 等
ci: sizet = s
while ci < e:
if buf[ci] == '*':
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
ci += 1
# void
if ty_len == 4 and string.strncmp(buf + s, "void", 4) == 0:
return llvmlite.Void(pool)
# double
if ty_len == 6 and string.strncmp(buf + s, "double", 6) == 0:
return llvmlite.Double(pool)
# float
if ty_len == 5 and string.strncmp(buf + s, "float", 5) == 0:
return llvmlite.Float(pool)
# iN (i8, i16, i32, i64)
if ty_len >= 2 and buf[s] == 'i':
bits: int = 0
di: sizet = s + 1
valid: int = 1
while di < e:
d: int = buf[di]
if d >= '0' and d <= '9':
bits = bits * 10 + (d - '0')
else:
valid = 0
break
di += 1
if valid != 0 and bits > 0:
if bits == 8:
return llvmlite.Int8(pool)
if bits == 16:
return llvmlite.Int16(pool)
if bits == 32:
return llvmlite.Int32(pool)
if bits == 64:
return llvmlite.Int64(pool)
# 无法解析
return None
# _LookupCachedRetTyText:查「依赖模块 text.ll 方法返回类型文本」持久缓存——键为 define 完整 mangled 名("{sha1}.{class}.{method}",bare 模式 "{sha1}.{method}"),命中返回文本,未命中/表未建返回 None
def _LookupCachedRetTyText(self, class_sha1: str, class_name: str,
method_name: str, use_bare: int) -> str:
"""查返回类型文本缓存:命中返回文本,未命中返回 None"""
if self.Gen is None or class_sha1 is None or method_name is None:
return None
ht: rawptr[hashtable.HashTable] = self.Gen.CallGenBase.MethodRetTyMap
if ht is None:
return None
sha1_len: sizet = string.strlen(class_sha1)
cls_len: sizet = 0
if class_name is not None:
cls_len = string.strlen(class_name)
meth_len: sizet = string.strlen(method_name)
# 长度公式与 _LookupMethodRetTyFromTextLL 构造 search_buf 时完全一致
key_len: sizet = sha1_len + meth_len + 2
if use_bare == 0:
key_len = sha1_len + cls_len + meth_len + 4
key_buf: bytes = stdlib.malloc(key_len)
if key_buf is None:
return None
if use_bare != 0:
viperlib.snprintf(key_buf, key_len, "%s.%s", class_sha1, method_name)
else:
viperlib.snprintf(key_buf, key_len, "%s.%s.%s",
class_sha1, class_name, method_name)
v: t.CPtr = ht.__getitem__(key_buf)
stdlib.free(key_buf)
if v is None:
return None
return (str | t.CPtr)(v)
# _CacheModuleRetTyTexts:把某模块 text.ll 全部 define 行的返回类型写入持久缓存(键/值深拷贝进 PermPool,同名只记首次,只登记 "{sha1}." 开头的本模块 mangled 名);仅在成功读到 text.ll 后调用,负结果绝不缓存
def _CacheModuleRetTyTexts(self, class_sha1: str, text_buf: bytes,
text_len: sizet) -> None:
"""登记 text.ll 全部 define 行到持久缓存(建表/分配失败即不缓存,回退原扫描)"""
if self.Gen is None or class_sha1 is None or text_buf is None or text_len == 0:
return
perm: rawptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
if perm is None:
return
ht: rawptr[hashtable.HashTable] = self.Gen.CallGenBase.MethodRetTyMap
if ht is None:
ht = hashtable.HashTable(perm)
if ht is None:
return
self.Gen.CallGenBase.MethodRetTyMap = ht
sha1_len: sizet = string.strlen(class_sha1)
pos: sizet = 0
while pos < text_len:
ls: sizet = pos
le: sizet = pos
while le < text_len:
if text_buf[le] == '\n':
break
le += 1
ll: sizet = le - ls
# define 行判定与 _LookupMethodRetTyFromTextLL 逐字一致
if ll >= 7 and text_buf[ls] == 'd' and string.strncmp(text_buf + ls, "define ", 7) == 0:
at_pos: sizet = ls + 7
while at_pos < ls + ll:
if text_buf[at_pos] == '@':
break
at_pos += 1
if at_pos < ls + ll:
name_start: sizet = at_pos + 1
quoted: int = 0
if name_start < ls + ll and text_buf[name_start] == '"':
name_start += 1
quoted = 1
# 定界:带引号名到 '"',裸名到 '('(与查找侧边界判定等价)
name_end: sizet = name_start
if quoted != 0:
while name_end < ls + ll:
if text_buf[name_end] == '"':
break
name_end += 1
else:
while name_end < ls + ll:
if text_buf[name_end] == '(':
break
name_end += 1
name_len: sizet = name_end - name_start
# 只登记本模块 mangled 名 "{sha1}.{后缀}"(查询键恒为此前缀)
if name_len > sha1_len + 1:
if string.strncmp(text_buf + name_start, class_sha1, sha1_len) == 0 \
and text_buf[name_start + sha1_len] == '.':
key_buf: bytes = stdlib.malloc(name_len + 1)
if key_buf is not None:
string.strncpy(key_buf, text_buf + name_start, name_len)
key_buf[name_len] = '\0'
# 同名首次为准(不被后续同名覆盖,与首个命中一致)
if ht.__contains__(key_buf) == 0:
rt_len: sizet = at_pos - (ls + 7)
kcopy: str = perm.alloc(name_len + 1)
tcopy: str = perm.alloc(rt_len + 1)
if kcopy is not None and tcopy is not None:
string.strncpy(kcopy, text_buf + name_start, name_len)
kcopy[name_len] = '\0'
string.strncpy(tcopy, text_buf + ls + 7, rt_len)
tcopy[rt_len] = '\0'
ht.set_str(kcopy, tcopy)
stdlib.free(key_buf)
pos = le + 1
# _LookupMethodRetTyFromTextLL:读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),按 "{sha1}.{class_name}.{method_name}" 搜索 define 行并解析返回类型;未找到/文件不存在返回 None
# ============================================================
def _LookupMethodRetTyFromTextLL(self, pool: rawptr[memhub.MemBuddy],
class_sha1: str,
class_name: str,
method_name: str,
use_bare: int = 0) -> rawptr[llvmlite.LLVMType]:
"""从依赖模块的 text.ll 中查找方法返回类型
use_bare: 1=按模块级函数查找(搜索 "{sha1}.{method_name}" define,
用于 new_lexer/tokenize 等模块函数;class_name 忽略)。
"""
if pool is None or class_sha1 is None or method_name is None:
return None
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0:
return None
sha1_len: sizet = string.strlen(class_sha1)
if sha1_len < 2:
return None
# 缓存命中:O(1) 取回返回类型文本并用调用方 pool 重建类型直接返回;未命中走下方原实现
cached_text: str = self._LookupCachedRetTyText(
class_sha1, class_name, method_name, use_bare)
if cached_text is not None:
return self._ParseLLVMRetTyStr(pool, cached_text, 0, string.strlen(cached_text))
# 构造 text.ll 路径 (level=1): {temp_dir}/{sha1[0:2]}/{sha1}.text.ll
# 使用 subdir 缓冲区避免 %c 格式化问题(与 StubMerger._sliced_path 一致)
path_len: sizet = self.Gen.CallGenBase.TempDirLen + sha1_len + 14
path_buf: bytes = stdlib.malloc(path_len)
if path_buf is None:
return None
subdir_buf: bytes = stdlib.malloc(3)
if subdir_buf is None:
stdlib.free(path_buf)
return None
subdir_buf[0] = class_sha1[0]
subdir_buf[1] = class_sha1[1]
subdir_buf[2] = '\0'
viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll",
self.Gen.CallGenBase.TempDir, subdir_buf, class_sha1)
stdlib.free(subdir_buf)
# 按文件实际大小读取 text.ll(固定缓冲区会截断大模块导致末尾定义查不到);64MB 上限防御异常超大文件
tf: rawptr[fileio.File] = fileio.File(path_buf, fileio.MODE.R)
if tf.closed:
stdlib.free(path_buf)
return None
tfsize: LONGLONG = tf.size()
if tfsize <= 0 or tfsize > 67108864:
tf.close()
stdlib.free(path_buf)
return None
read_size: sizet = sizet(tfsize)
read_buf: bytes = stdlib.malloc(read_size + 1)
if read_buf is None:
tf.close()
stdlib.free(path_buf)
return None
text_len: int64 = tf.read_all(read_buf, ULONG(read_size + 1))
tf.close()
stdlib.free(path_buf)
if text_len <= 0:
stdlib.free(read_buf)
return None
read_buf[text_len] = '\0'
# 构造搜索模式: "{sha1}.{class_name}.{method_name}" 或 bare 模式 "{sha1}.{method_name}"
cls_len: sizet = 0
if class_name is not None:
cls_len = string.strlen(class_name)
meth_len: sizet = string.strlen(method_name)
search_len: sizet = sha1_len + meth_len + 2
if use_bare == 0:
search_len = sha1_len + cls_len + meth_len + 4
search_buf: bytes = stdlib.malloc(search_len)
if search_buf is None:
stdlib.free(read_buf)
return None
if use_bare != 0:
viperlib.snprintf(search_buf, search_len, "%s.%s",
class_sha1, method_name)
else:
viperlib.snprintf(search_buf, search_len, "%s.%s.%s",
class_sha1, class_name, method_name)
search_str_len: sizet = string.strlen(search_buf)
# 在 text.ll 中搜索 define 行包含 search_buf
buf_len: sizet = string.strlen(read_buf)
# 已成功读到本模块 text.ll:登记其全部 define 行供后续 O(1) 命中(下方原扫描保留为未命中路径)
self._CacheModuleRetTyTexts(class_sha1, read_buf, buf_len)
pos: sizet = 0
result_ty: rawptr[llvmlite.LLVMType] = None
while pos < buf_len and result_ty is None:
ls: sizet = pos
le: sizet = pos
while le < buf_len:
if read_buf[le] == '\n':
break
le += 1
ll: sizet = le - ls
# define 行判定:快速路径先看行首字节是否 'd'(仅否定短路,不改变命中结果)
if ll >= 7 and read_buf[ls] == 'd' and string.strncmp(read_buf + ls, "define ", 7) == 0:
# 找 @ 符号
at_pos: sizet = ls + 7
while at_pos < ls + ll:
if read_buf[at_pos] == '@':
break
at_pos += 1
if at_pos < ls + ll:
# 跳过可选引号
name_start: sizet = at_pos + 1
if name_start < ls + ll and read_buf[name_start] == '"':
name_start += 1
# 检查是否匹配 search_buf
if name_start + search_str_len <= ls + ll:
if string.strncmp(read_buf + name_start, search_buf, search_str_len) == 0:
# 函数名边界检查:search 后必须是 '"'(带引号名)、'('(参数表)或行尾,
# 否则 "sha1.func" 会误匹配 "sha1.func_helper" 等前缀同名函数
name_end: sizet = name_start + search_str_len
end_ch: int = 0
if name_end < ls + ll:
end_ch = read_buf[name_end]
if end_ch == 34 or end_ch == 40 or end_ch == 0:
# 匹配!提取返回类型: [ls+7, at_pos)
result_ty = self._ParseLLVMRetTyStr(pool, read_buf, ls + 7, at_pos)
pos = le + 1
stdlib.free(search_buf)
stdlib.free(read_buf)
return result_ty
# _dep_text_ll_exists:探测 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll 是否存在(1=存在),用于区分「依赖已编译但函数缺失」与「依赖模块尚未翻译」
# ============================================================
def _dep_text_ll_exists(self, pool: rawptr[memhub.MemBuddy],
sha1: str) -> int:
"""探测 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll 是否存在,1=存在"""
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0 or sha1 is None:
return 0
sha1_len: sizet = string.strlen(sha1)
if sha1_len < 2:
return 0
path_len: sizet = self.Gen.CallGenBase.TempDirLen + sha1_len + 14
path_buf: bytes = stdlib.malloc(path_len)
subdir_buf: bytes = stdlib.malloc(3)
if path_buf is None or subdir_buf is None:
if path_buf is not None:
stdlib.free(path_buf)
if subdir_buf is not None:
stdlib.free(subdir_buf)
return 0
subdir_buf[0] = sha1[0]
subdir_buf[1] = sha1[1]
subdir_buf[2] = '\0'
viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll",
self.Gen.CallGenBase.TempDir, subdir_buf, sha1)
stdlib.free(subdir_buf)
# 存在性判定用 GetFileAttributesA(属性查询,不开文件句柄)
attr: ULONG = win32file.GetFileAttributesA(path_buf)
exists: int = 1
# INVALID_FILE_ATTRIBUTES = 不存在/不可访问 → 0。
if attr == 0xFFFFFFFF:
exists = 0
# 原实现以只读 CreateFileA 打开:目录会被拒绝(未带 FILE_FLAG_BACKUP_SEMANTICS)
# → 0,故此处显式排除目录以保持「是可读常规文件」的等价判定。
if (attr & win32file.FILE_ATTRIBUTE_DIRECTORY) != 0:
exists = 0
stdlib.free(path_buf)
return exists
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助:把 obj.prop / obj.prop=v / del obj.prop / Class.method() 翻译为 {sha1}.ClassName.prop 系列函数调用(元数据表由 HandlesClassDef 填充)
# _translate_property_call:翻译属性方法调用——优先用模块中已有函数(精确/后缀匹配),否则用类所属模块 SHA1 构造 "{sha1}.ClassName.method";call_head/call_count 为已构建好的参数链表(如 getter 只有 obj 一个参数)