1297 lines
64 KiB
Plaintext
1297 lines
64 KiB
Plaintext
import lib.core.Gen.GenTypes as GenTypes
|
||
import lib.core.Gen.Generator as Generator
|
||
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import llvmlite
|
||
import memhub
|
||
import string
|
||
import stdio
|
||
import stdlib
|
||
import viperlib
|
||
import w32.fileio as fileio
|
||
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: Generator.Generator | t.CPtr
|
||
CexportSha1Arr: bytes
|
||
CexportFuncArr: bytes
|
||
CexportCount: int
|
||
|
||
# --- LLVMIR 寄存器序号(原 _g_llvmir_reg_seq) ---
|
||
LLVMIRRegSeq: int
|
||
|
||
# --- temp 目录(原 _g_temp_dir/_g_temp_dir_len) ---
|
||
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: GenTypes.FuncEntry | t.CPtr
|
||
GlobalFuncCount: int
|
||
|
||
# --- 全局默认参数表(原 _global_defaults/_global_defaults_count) ---
|
||
GlobalDefaults: GenTypes.FuncDefaultsEntry | t.CPtr
|
||
GlobalDefaultsCount: int
|
||
|
||
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.GlobalDefaults = None
|
||
self.GlobalDefaultsCount = 0
|
||
return 0
|
||
|
||
# ============================================================
|
||
# P4 簇 1+2 迁移方法(函数体来自 HandlesExprCall.vp:
|
||
# 模块 SHA1 查询/CExport 注册表/c.* 系列/内联汇编/LLVMIR)
|
||
# 本模块函数调用改 self.,外部模块调用保留模块前缀
|
||
# ============================================================
|
||
|
||
|
||
|
||
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: memhub.MemBuddy | t.CPtr,
|
||
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: memhub.MemBuddy | t.CPtr,
|
||
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 * 无法确定
|
||
# func_name 归属,收集全部候选由调用方逐个验证(is_cexport_func /
|
||
# _is_state_func_from_vpi 按 vpi 返回注解确认定义模块)。
|
||
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: fileio.File | t.CPtr = 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 行匹配之前,对所有行生效):
|
||
# __values.vp 自含 def ConstFloat 且 `from stdint import *`——
|
||
# star 候选 stdint 会把函数归属劫持为 f5522571bcce7bcb.ConstFloat
|
||
# (跨模块返回类型解析 fail-fast 根因)。模块级 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 * → 任意函数都可能属 X 模块。
|
||
# 不立即返回(首个 star 行未必是 func_name 的定义模块,如
|
||
# fltk/__init__ 依次 from _types/_enums/_cfl import *),累积
|
||
# 全部候选,扫描完由调用方逐个验证(is_cexport_func /
|
||
# _is_state_func_from_vpi 按 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
|
||
# 全部行扫描完毕:显式 from-import 精确匹配已提前返回;仅有 star
|
||
# import 时返回累积候选(空格分隔,调用方逐个验证定义模块)。
|
||
# 但模块自含 def {func_name}(未遇同名 from-import 覆盖,否则上面
|
||
# 已提前返回 import 源)时本模块就是定义者,不是 re-export——star
|
||
# 候选(如 __values.vp `from stdint import *` 的 stdint)会劫持归属,
|
||
# 生成错误 SHA1(f5522571bcce7bcb.ConstFloat)→ 跨模块返回类型解析
|
||
# fail-fast。返回 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: memhub.MemBuddy | t.CPtr,
|
||
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: memhub.MemBuddy | t.CPtr,
|
||
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: memhub.MemBuddy | t.CPtr,
|
||
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: llvmlite.LLVMModule | t.CPtr,
|
||
name: str,
|
||
owner_sha1: str = None) -> llvmlite.Function | t.CPtr:
|
||
"""在模块的函数链表中按名称查找函数
|
||
|
||
查找策略:
|
||
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)
|
||
cur: llvmlite.Function | t.CPtr = mod.FuncHead
|
||
# 第一遍:精确匹配
|
||
while cur is not None:
|
||
cur_name: str = llvmlite.function_get_name(cur)
|
||
if cur_name is not None:
|
||
if string.strcmp(cur_name, name) == 0:
|
||
return cur
|
||
cur = llvmlite.function_get_next(cur)
|
||
# 第一遍半(owner_sha1 提供时):本模块同名函数优先。
|
||
# 仅接受恰为 "{owner_sha1}.{name}" 的形态(mangled 模块级函数名含
|
||
# 且只含一个 '.'),不匹配 "{sha1}.Class.method" 等方法名。是否
|
||
# IsDeclared==0 均可返回:前向 declare 与 define 同名同符号。
|
||
if owner_sha1 is not None:
|
||
cur = mod.FuncHead
|
||
while cur is not None:
|
||
cur_name_o: str = llvmlite.function_get_name(cur)
|
||
if cur_name_o is not None:
|
||
cur_len_o: sizet = string.strlen(cur_name_o)
|
||
if cur_len_o == 17 + name_len:
|
||
if string.strncmp(cur_name_o, owner_sha1, 16) == 0 and cur_name_o[16] == '.':
|
||
suffix_start_o: sizet = cur_len_o - name_len
|
||
if cur_name_o[suffix_start_o - 1] == '.':
|
||
match_o: int = 1
|
||
for i in range(name_len):
|
||
if cur_name_o[suffix_start_o + i] != name[i]:
|
||
match_o = 0
|
||
break
|
||
if match_o == 1:
|
||
return cur
|
||
cur = llvmlite.function_get_next(cur)
|
||
# 第二遍:后缀匹配(.name)
|
||
# 不跳过 declare:方法 stub 也是 declare,需要通过后缀匹配找到
|
||
# 调用处(translate_call 等)负责判断 IsDeclared 并决定使用裸名还是 sha1 前缀名
|
||
#
|
||
# 修复:本模块 define 优先于跨模块 stub declare。
|
||
# 根因:跨模块同名函数(如 lexer.vp 的 _advance 与 parser.vp 的 _advance)
|
||
# 的 stub declare 在 stub 注入时排在 FuncHead 前面,裸名调用 _advance 后缀
|
||
# 匹配按链表顺序先命中跨模块 declare → 参数按 Lexer* coerce → Parser* 被
|
||
# bitcast 成 Lexer* → lexer 版 _advance 按 Lexer 布局操作 Parser 指针,
|
||
# cur 永不推进 → parse 无限循环。本模块同名 define 必须优先。
|
||
cur = mod.FuncHead
|
||
while cur is not None:
|
||
cur_name2: str = llvmlite.function_get_name(cur)
|
||
if cur_name2 is not None:
|
||
cur_len: sizet = string.strlen(cur_name2)
|
||
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 cur.IsDeclared == 0:
|
||
return cur
|
||
cur = llvmlite.function_get_next(cur)
|
||
# 本模块无同名 define:回退到 stub declare(跨模块方法/函数调用)
|
||
cur = mod.FuncHead
|
||
while cur is not None:
|
||
cur_name3: str = llvmlite.function_get_name(cur)
|
||
if cur_name3 is not None:
|
||
cur_len3: sizet = string.strlen(cur_name3)
|
||
if cur_len3 > name_len + 1:
|
||
suffix_start3: sizet = cur_len3 - name_len
|
||
if cur_name3[suffix_start3 - 1] == '.':
|
||
match3: int = 1
|
||
for i in range(name_len):
|
||
if cur_name3[suffix_start3 + i] != name[i]:
|
||
match3 = 0
|
||
break
|
||
if match3 == 1:
|
||
return cur
|
||
cur = llvmlite.function_get_next(cur)
|
||
return None
|
||
|
||
|
||
# ============================================================
|
||
# _ensure_method_declare - 为跨模块方法调用创建 declare 声明
|
||
#
|
||
# 当 found_func 为 None(stub 未注入或跨模块调用)时,
|
||
# 在当前模块中创建函数声明,避免 llc 报 undefined value。
|
||
# ============================================================
|
||
def _ensure_method_declare(self, pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
call_name: str,
|
||
ret_ty: llvmlite.LLVMType | t.CPtr,
|
||
param_head: llvmlite.ParamNode | t.CPtr,
|
||
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: llvmlite.Function | t.CPtr = 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:
|
||
if 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: llvmlite.Param | t.CPtr = 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: llvmlite.ParamNode | t.CPtr = 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: llvmlite.Param | t.CPtr = 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、stdlib.memcpy → @memcpy):
|
||
# call_name 无 sha1 前缀 → 无法提取定义侧模块查 vpi 参数注解 →
|
||
# 此前用实参类型建 declare(字面量 0=i32 把定义 sizet(i64) 固化窄,
|
||
# VKernel process.vp string.memset 声明 @memset(i8*,i32,i32) vs 定义
|
||
# @memset(i8*,i32,i64),跨模块 ABI 宽度不一致,优化路径读高位垃圾
|
||
# 写穿内存)。已知 C 库函数直接按标准签名建 declare(与 CallGenLib
|
||
# _ensure_c_lib_declare 白名单一致:memset/memcpy/memmove/memcmp/
|
||
# strcmp/strncmp/strcpy/strncpy/strcat/strlen/puts/atoi/atol/abs/labs)。
|
||
if call_name is not None and string.strchr(call_name, 46) is None:
|
||
std_lib_fn: llvmlite.Function | t.CPtr = self.Gen.CallGenLib._ensure_c_lib_declare(
|
||
pool, mod, call_name)
|
||
if std_lib_fn is not None:
|
||
return
|
||
# 创建函数声明
|
||
func: llvmlite.Function | t.CPtr = llvmlite.new_function(pool, call_name, ret_ty)
|
||
if func is None:
|
||
return
|
||
func.IsDeclared = 1
|
||
# 定义侧参数类型优先(根治实参字面量固化错误宽度):
|
||
# declare 此前用调用点实参类型(param_head)建参数,字面量 0=i32 会把
|
||
# 定义 sizet(i64) 的参数固化窄(SlotsBugTest _translate_struct_ctor
|
||
# can=i32 vs 定义 i64 → 调用侧 trunc i64→i32、callee 按 i64 读栈残留
|
||
# 垃圾高位 → can=巨值读未写槽位崩溃根因)。从 call_name 提取
|
||
# {sha1}.{cls}.{method} 查 vpi 参数注解缓存取定义侧类型:仅当实参与
|
||
# 定义都是整数且定义更宽时采用定义宽度(指针/联合保持实参类型,避免
|
||
# 字符串路径把 list*/Struct* 降级为 i8*)。
|
||
# 注:反方向(定义侧更窄,如 su8250.putchar 定义 (i16,i8) 而字面量
|
||
# 1016=i32)由调用点 CallGenLib._coerce_args_to_def_params 先把实参
|
||
# 窄化到定义宽度,本处依 param_head 建 declare 即已对齐(此块做补强,
|
||
# 对已窄化的实参不再触发)。
|
||
sha1_d: str = None
|
||
cls_d: str = None
|
||
meth_d: str = None
|
||
if call_name is not None:
|
||
cl_len_d: sizet = string.strlen(call_name)
|
||
if cl_len_d >= 17 and call_name[16] == '.':
|
||
sha1_ok_d: int = 1
|
||
si_d: sizet
|
||
for si_d in range(16):
|
||
cc_d: int = call_name[si_d]
|
||
if not ((cc_d >= 48 and cc_d <= 57) or (cc_d >= 97 and cc_d <= 102)):
|
||
sha1_ok_d = 0
|
||
break
|
||
if sha1_ok_d != 0:
|
||
sha1_buf_d: bytes = stdlib.malloc(17)
|
||
if sha1_buf_d is not None:
|
||
string.strncpy(sha1_buf_d, call_name, 16)
|
||
sha1_buf_d[16] = '\0'
|
||
sha1_d = sha1_buf_d
|
||
rest_d: str = call_name + 17
|
||
dot2_d: str = string.strchr(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: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||
i8_ptr_d: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||
# 添加参数(declare 中参数名无意义,设为 None 只输出类型)
|
||
pnode: llvmlite.ParamNode | t.CPtr = param_head
|
||
pi: int = 0
|
||
while pnode is not None and pi < param_count:
|
||
param_ty_d: llvmlite.LLVMType | t.CPtr = pnode.Ty
|
||
if prm_annots_d is not None and prm_annots_d[0] != '\0' and pnode.Ty is not None:
|
||
seg_ty_d: llvmlite.LLVMType | t.CPtr = 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: llvmlite.Param | t.CPtr = 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 - 从 vpi stub 返回注解缓存精确获取方法返回类型
|
||
#
|
||
# 方法定义处(.vpi stub)的返回注解(如 json.JsonValue._pool_alloc →
|
||
# "str")在 stub 注入时由 VpiReader 缓存(_cache_vpi_return_annots_text)。
|
||
# 此函数按 模块SHA1 + 类名 + 方法名 查缓存并纯字符串解析为 LLVMType。
|
||
# 无需硬编码白名单——返回类型始终来自方法定义处。
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
def _lookup_method_ret_ty_via_vpi(self, pool: memhub.MemBuddy | t.CPtr,
|
||
cls_sha1: str,
|
||
cls_name: str,
|
||
method_name: str) -> llvmlite.LLVMType | t.CPtr:
|
||
"""从 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: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = 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 参数注解缓存获取方法参数注解串
|
||
#
|
||
# 跨模块方法调用在 stub 未注入(依赖收集不加载被调模块 vpi,如
|
||
# self.Gen.ExprGenCdef.method 经 Generator 属性访问)时,函数表无
|
||
# ParamCount/默认参数信息 → 调用点缺参 → 被调函数读栈残留。此函数
|
||
# 按需加载并查询 vpi 参数注解缓存('@' 分隔,含首参 self 空串占位),
|
||
# 调用方据此补全 declare/call 缺失参数(默认参数零值兜底)。
|
||
# 未命中返回 None。
|
||
# ============================================================
|
||
def _vpi_method_param_annot(self, pool: memhub.MemBuddy | t.CPtr,
|
||
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
|
||
#
|
||
# 与 _lookup_method_ret_ty_via_vpi 的返回注解解析同源;解析失败返回
|
||
# None,调用方以 i8* 兜底(ABI 兼容,参数槽位宽度一致)。
|
||
# ============================================================
|
||
def _vpi_resolve_param_annot_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||
seg: str) -> llvmlite.LLVMType | t.CPtr:
|
||
"""解析单个 vpi 参数注解段为 LLVMType,失败返回 None"""
|
||
if pool is None or seg is None:
|
||
return None
|
||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = 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)(存储空间指针)
|
||
#
|
||
# 这些方法不存在于源码(vpi stub 无条目),跨模块调用时
|
||
# found_func/text.ll/vpi 缓存必然未命中,但返回类型是编译器
|
||
# 确定性语义(与 HandlesClassDef._translate_method 3543-3548 一致),
|
||
# 在此直接固定,不触发 fail-fast 误报。非特殊方法返回 None。
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
def _special_method_ret_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||
class_name: str,
|
||
method_name: str) -> llvmlite.LLVMType | t.CPtr:
|
||
"""编译器内置特殊方法的固定返回类型;非特殊返回 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: llvmlite.LLVMType | t.CPtr = 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 - 方法返回类型无法精确获取时 fail-fast
|
||
#
|
||
# 返回类型来源(按序):当前模块函数 → text.ll define → vpi stub 返回注解缓存。
|
||
# 全部未命中时立即 ERROR + 错误堆栈终止,绝不按名推断白名单/静默默认 i32。
|
||
#
|
||
# node: 调用点 AST 节点(可选,用于 ERROR 堆栈定位)
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
def _fail_method_ret_ty(self, method_name: str,
|
||
node: ast.AST | t.CPtr = None) -> llvmlite.LLVMType | t.CPtr:
|
||
"""方法返回类型无法精确获取时 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 目录路径
|
||
#
|
||
# 在 Phase2.py 的 Phase A 翻译前调用,使 CallGenMixin
|
||
# 能够读取依赖模块的 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 返回类型字符串转换为 llvmlite.LLVMType
|
||
#
|
||
# 从 define 行中提取的返回类型字符串(define 与 @ 之间的内容),
|
||
# 转换为 llvmlite.LLVMType 对象。
|
||
#
|
||
# 支持的类型:
|
||
# void → Void
|
||
# i8/i16/i32/i64 → Int8/Int16/Int32/Int64
|
||
# double → Double, float → Float
|
||
# 任何含 * 的类型 → Ptr(Int8)(通用指针,保留 64 位地址)
|
||
#
|
||
# Args:
|
||
# pool: 内存池
|
||
# buf: 包含类型字符串的缓冲区
|
||
# start: 类型字符串起始位置
|
||
# end: 类型字符串结束位置(不含)
|
||
#
|
||
# Returns:
|
||
# LLVM 类型对象,或 None(无法解析)
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
def _ParseLLVMRetTyStr(self, pool: memhub.MemBuddy | t.CPtr,
|
||
buf: bytes,
|
||
start: sizet,
|
||
end: sizet) -> llvmlite.LLVMType | t.CPtr:
|
||
"""将 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 打包返回类型,如 b2a_hqx ->
|
||
# tuple[str, t.CUnsignedShort] 编译为 {i8*, i16})。必须在 * 检查
|
||
# 之前——否则 "{i8*, i16}" 内含 * 会被误判为 i8*(b2a_hqx 调用侧
|
||
# 返回类型退化 i8* → HandlesAssign tuple 解包静默跳过 → free 未初始化
|
||
# 指针崩溃,HashLibTest hqx 段)。
|
||
if s < e and buf[s] == '{':
|
||
th_st: llvmlite.ParamNode | t.CPtr = None
|
||
tt_st: llvmlite.ParamNode | t.CPtr = 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: llvmlite.LLVMType | t.CPtr = self._ParseLLVMRetTyStr(
|
||
pool, buf, es_st, ee_st)
|
||
if et_st is None:
|
||
return None
|
||
en_st: llvmlite.ParamNode | t.CPtr = 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: llvmlite.LLVMType | t.CPtr = 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
|
||
|
||
# ============================================================
|
||
# _LookupMethodRetTyFromTextLL - 从依赖模块的 text.ll 查找方法返回类型
|
||
#
|
||
# 读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),搜索
|
||
# define 行中包含 "{sha1}.{class_name}.{method_name}" 的函数,
|
||
# 提取返回类型并转换为 llvmlite.LLVMType。
|
||
#
|
||
# 解决问题: 旧实现 _infer_method_ret_ty(已移除)默认返回 i32,导致指针返回值
|
||
# 被 inttoptr 截断为 32 位(coerce_to_type line 238-239)。
|
||
# 本函数从 text.ll 获取实际返回类型,避免截断。
|
||
#
|
||
# Args:
|
||
# pool: 内存池
|
||
# class_sha1: 类所属模块的 SHA1
|
||
# class_name: 类名
|
||
# method_name: 方法名
|
||
#
|
||
# Returns:
|
||
# LLVM 类型对象,或 None(未找到/text.ll 不存在)
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
def _LookupMethodRetTyFromTextLL(self, pool: memhub.MemBuddy | t.CPtr,
|
||
class_sha1: str,
|
||
class_name: str,
|
||
method_name: str,
|
||
use_bare: int = 0) -> llvmlite.LLVMType | t.CPtr:
|
||
"""从依赖模块的 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
|
||
|
||
# 构造 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(按文件实际大小分配:固定 _TEXT_LL_READ_BUF_SIZE(256KB)
|
||
# 会截断大模块的 text.ll,末尾方法定义查不到 → 误报"方法返回类型无法
|
||
# 精确获取"(追加80b 根因,同追加74 _read_source_line 截断)。64MB 上限
|
||
# 防御异常超大文件整体读取。)
|
||
tf: fileio.File | t.CPtr = 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)
|
||
pos: sizet = 0
|
||
result_ty: llvmlite.LLVMType | t.CPtr = 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 行
|
||
if ll >= 7 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 - 探测依赖模块 text.ll 是否已生成
|
||
#
|
||
# 用于区分跨模块返回类型精确解析失败的两种场景:
|
||
# 1. text.ll 存在但函数未命中 → 真错误(依赖已编译,函数缺失)
|
||
# 2. text.ll 不存在 → 依赖模块尚未翻译(Phase A 按文件顺序翻译,
|
||
# main.vp 先于其 import 的 mbuddytest.vp),标记重试待下遍
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
def _dep_text_ll_exists(self, pool: memhub.MemBuddy | t.CPtr,
|
||
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)
|
||
tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
|
||
exists: int = 0
|
||
if not tf.closed:
|
||
tf.close()
|
||
exists = 1
|
||
stdlib.free(path_buf)
|
||
return exists
|
||
|
||
# ============================================================
|
||
# _AppendPhaseAPending - 追加 Phase A MRO/VPI pending 明细到诊断文件
|
||
#
|
||
# 跨模块方法返回类型解析(mro_ret / mro_anc)在依赖模块 text.ll 未生成
|
||
# 时置 _deps_pending。此信息此前只走滚动 DEPDBG(刷屏易被淹没),此处
|
||
# 持久化到 {TempDir}/_phaseA_pending.log(tab 分隔),供 Phase2 收敛失败
|
||
# 时按调用方 sha1 过滤,输出"MRO/VPI 类似依赖图"的最终诊断。
|
||
# 行格式: {owner_sha1}\t{kind}\t{cls}\t{method}\t{dep_sha1}\t{extra}\n
|
||
# ============================================================
|
||
def _AppendPhaseAPending(self, owner_sha1: str, kind: str,
|
||
cls_name: str, method_name: str,
|
||
dep_sha1: str, extra: str) -> None:
|
||
"""追加一行 MRO/VPI pending 明细到诊断文件(文件不存在则创建)"""
|
||
td: str = self.Gen.CallGenBase.TempDir
|
||
td_len: sizet = self.Gen.CallGenBase.TempDirLen
|
||
if td is None or td_len == 0 or owner_sha1 is None or kind is None:
|
||
return
|
||
path_len: sizet = td_len + 24
|
||
path_buf: bytes = stdlib.malloc(path_len)
|
||
if path_buf is None:
|
||
return
|
||
viperlib.snprintf(path_buf, path_len, "%s/_phaseA_pending.log", td)
|
||
pf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.A)
|
||
stdlib.free(path_buf)
|
||
if pf.closed:
|
||
return
|
||
line: bytes = stdlib.malloc(512)
|
||
if line is not None:
|
||
viperlib.snprintf(line, 512, "%s\t%s\t%s\t%s\t%s\t%s\n",
|
||
owner_sha1, kind,
|
||
cls_name if cls_name is not None else "-",
|
||
method_name if method_name is not None else "-",
|
||
dep_sha1 if dep_sha1 is not None else "-",
|
||
extra if extra is not None else "-")
|
||
ln: sizet = string.strlen(line)
|
||
pf.write(line, ln)
|
||
stdlib.free(line)
|
||
pf.close()
|
||
|
||
# ============================================================
|
||
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
|
||
#
|
||
# 元数据表由 HandlesClassDef 在类翻译/预注册阶段填充,
|
||
# 这里负责把 obj.prop / obj.prop=v / del obj.prop / Class.method()
|
||
# 翻译为对应的 {sha1}.ClassName.prop 系列函数调用。
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# _translate_property_call — 翻译属性方法调用
|
||
#
|
||
# 与 _translate_method_call 的调用名/返回类型解析逻辑一致:
|
||
# 1. 优先用模块中已有的函数(精确/后缀匹配,来自 stub 注入)
|
||
# 2. 否则用类所属模块的 SHA1 构造 "{sha1}.ClassName.method" 调用名
|
||
# 3. 返回类型:函数实类型 → text.ll 查找 → 按方法名推断(默认 i32)
|
||
#
|
||
# call_head/call_count: 已构建好的参数链表(如 getter 只有 obj 一个参数)
|
||
# ============================================================
|