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

1014 lines
48 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 t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
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(最底层父类):模块符号查找 +
# 返回类型解析基础设施
#
# 按功能域从 CallGenMixin.vp 拆出(500~1500 行/文件架构重构):
# 字段(15 个)+ InitCallGen + 模块符号查找(sha1/cexport/vpi/reexport/find_func_in_module)
# + 返回类型解析链(_ensure_method_declare/_LookupMethodRetTyFromTextLL/_dep_text_ll_exists 等)
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# 常量(与 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: t.CSizeT
# --- 闭包名表(原 _g_closure_names/_g_closure_count) ---
ClosureNames: str
ClosureCount: int
# --- 函数指针签名表(原 _g_funcptr_names/_g_funcptr_rets/_g_funcptr_count) ---
FuncptrNames: str
FuncptrRets: t.CUInt64T | 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 | t.CPtr = StubMergerStore.GetSha1StoreRelArrPtr()
sha1_arr_ptr: bytes | t.CPtr = 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: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0:
continue
ridx: t.CSizeT = t.CSizeT(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: t.CSizeT = t.CSizeT(t.CUInt64T(last_slash) - t.CUInt64T(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
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
if temp_dir is None:
return None
td_len: t.CSizeT = 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: t.CSizeT = 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
else:
buf[READ_SZ - 1] = 0
name_len: t.CSizeT = string.strlen(func_name)
total: t.CSizeT = string.strlen(buf)
pos: t.CSizeT = 0
while pos < total:
ls: t.CSizeT = pos
while pos < total and buf[pos] != '\n':
pos += 1
ll: t.CSizeT = pos - ls
if pos < total:
pos += 1
# 行必须以 "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: t.CSizeT = 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: t.CSizeT = ls + 5
mod_end: t.CSizeT = imp_pos
# 名称列表从 "import " 之后(逗号分隔,含 "A as B" 取原名 A)
nm_start: t.CSizeT = imp_pos + 8
scan: t.CSizeT = 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: t.CSizeT = scan
while n_end < ls + ll and buf[n_end] != ',' and buf[n_end] != ' ':
n_end += 1
cand_start: t.CSizeT = scan
cand_len: t.CSizeT = n_end - scan
as_pos: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT = 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
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.get_perm_pool().alloc(_CEXPORT_MAX * 17)
self.Gen.CallGenBase.CexportFuncArr = CompilerFactory.get_perm_pool().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: t.CSizeT = t.CSizeT(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: t.CSizeT = t.CSizeT(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 真源)"""
if self.Gen is None or sha1 is None or func_name is None:
return 0
if self.Gen.CallGenBase.CexportSha1Arr is None:
return 0
for i in range(self.Gen.CallGenBase.CexportCount):
idx: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0:
idx2: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name) == 0:
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"(如 "t.CSizeT | 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: t.CSizeT = 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) -> llvmlite.Function | t.CPtr:
"""在模块的函数链表中按名称查找函数
查找策略:
1. 精确匹配(如 "strlen" 匹配 @strlen,有 t.CExport 的函数)
2. 后缀匹配(如 "atoi" 匹配 @"83210a72e829a086.atoi",无 t.CExport 的函数)
使用 llvmlite.function_get_name/next 访问器绕过 stub 类型限制
(stub 类型字段不足且字段类型可能错误,直接字段访问会被静默跳过)。
"""
if mod is None or name is None:
return None
name_len: t.CSizeT = 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)
# 第二遍:后缀匹配(.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: t.CSizeT = string.strlen(cur_name2)
if cur_len > name_len + 1:
suffix_start: t.CSizeT = 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: t.CSizeT = string.strlen(cur_name3)
if cur_len3 > name_len + 1:
suffix_start3: t.CSizeT = 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
# 创建函数声明
func: llvmlite.Function | t.CPtr = llvmlite.new_function(pool, call_name, ret_ty)
if func is None:
return
func.IsDeclared = 1
# 添加参数(declare 中参数名无意义,设为 None 只输出类型)
pnode: llvmlite.ParamNode | t.CPtr = param_head
pi: int = 0
while pnode is not None and pi < param_count:
if pnode.Ty is not None:
param: llvmlite.Param | t.CPtr = llvmlite.new_param(pool, pnode.Ty, None)
if param is not None:
llvmlite.function_add_param(func, param)
pnode = pnode.Next
pi += 1
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)
# ============================================================
# _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):
"""设置 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: t.CSizeT,
end: t.CSizeT) -> llvmlite.LLVMType | t.CPtr:
"""将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType"""
if pool is None or buf is None or start >= end:
return None
# 去除前导空白
s: t.CSizeT = start
while s < end:
c: int = buf[s]
if c != ' ' and c != '\t':
break
s += 1
# 去除尾部空白
e: t.CSizeT = end
while e > s:
c2: int = buf[e - 1]
if c2 != ' ' and c2 != '\t':
break
e -= 1
ty_len: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT = ps_st
while ps_st < e and buf[ps_st] != ',' and buf[ps_st] != '}':
ps_st += 1
ee_st: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT = t.CSizeT(tfsize)
read_buf: bytes = stdlib.malloc(read_size + 1)
if read_buf is None:
tf.close()
stdlib.free(path_buf)
return None
text_len: t.CInt64T = 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: t.CSizeT = 0
if class_name is not None:
cls_len = string.strlen(class_name)
meth_len: t.CSizeT = string.strlen(method_name)
search_len: t.CSizeT = 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: t.CSizeT = string.strlen(search_buf)
# 在 text.ll 中搜索 define 行包含 search_buf
buf_len: t.CSizeT = string.strlen(read_buf)
pos: t.CSizeT = 0
result_ty: llvmlite.LLVMType | t.CPtr = None
while pos < buf_len and result_ty is None:
ls: t.CSizeT = pos
le: t.CSizeT = pos
while le < buf_len:
if read_buf[le] == '\n':
break
le += 1
ll: t.CSizeT = le - ls
# 检查是否是 define 行
if ll >= 7 and string.strncmp(read_buf + ls, "define ", 7) == 0:
# 找 @ 符号
at_pos: t.CSizeT = ls + 7
while at_pos < ls + ll:
if read_buf[at_pos] == '@':
break
at_pos += 1
if at_pos < ls + ll:
# 跳过可选引号
name_start: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT = string.strlen(sha1)
if sha1_len < 2:
return 0
path_len: t.CSizeT = 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
# ============================================================
# 属性方法(@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 一个参数)
# ============================================================