Files
ViperC/App/lib/Stub/StubGen/VpiReader.vp
T

2109 lines
108 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.GenTypes as GenTypes
import lib.core.CompilerFactory as CompilerFactory
import t, c
from stdint import *
import ast
import string
import stdio
import stdlib
import sys
import memhub
import llvmlite
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesTranslator as HT
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.Handles.ExprCallTable as ExprCallTable
# ============================================================
# 方法返回注解缓存(stdlib 持久)
#
# 背景:vpi tree 在"解析后立即"有效,但遍历处理到后续类的方法时,
# 深层 AST 节点(如 parse_args 的 returns 注解)可能已被覆盖损坏
# (kind() 读取异常),直接 resolve 得到垃圾类型(如 i8*)。
# 因此采用文本提取方案:ParseVpiIntoModule 读 .vpi stub 时,从
# vpi_buf 原始文本(在函数末尾才释放,始终有效)直接按行格式
# 提取 "def m(...) -> Ret:" 的返回注解字符串缓存到 stdlib(持久,
# 不受 pool 状态影响)。HandlesExprCall._lookup_method_ret_ty_via_vpi
# 按 sha1.类名.方法名 查表并用 _resolve_vpi_ret_annot_str 纯字符串
# 解析为 LLVMType,返回类型始终来自方法定义处,无白名单/无硬编码。
# ============================================================
VPI_RET_MAX: t.CDefine = 4096
VPI_KEY_LEN: t.CDefine = 224
VPI_RET_LEN: t.CDefine = 224
_g_vpi_ret_keys: bytes = None
_g_vpi_ret_strs: bytes = None
_g_vpi_ret_count: int = 0
_g_vpi_ret_cap: int = 0
# ============================================================
# 方法参数注解缓存(stdlib 持久,仿返回注解缓存)
#
# 背景:vpi tree 在"解析后立即"有效,但遍历处理到后续类的方法时,
# 深层 AST 节点(含函数参数列表)可能被覆盖损坏 → _inject_method_declare
# /_inject_function_declare 读 args 列表被截断(带默认值的尾部参数丢失,
# 同一 vpi 不同模块注入时忽对忽错)→ declare 参数数不足 → 调用点按
# declare 缺参(fill 无法超 declare 补)→ 被调函数按 x64 ABI 读残留
# 寄存器/栈槽垃圾(如"跨模块 CDefine 查找递归过深 depth=160":depth
# 与 imported_modules 均为垃圾,导致 imported_modules 指针失控递归)。
# 与返回注解同源:从 vpi_buf 原始文本(ParseVpiIntoModule 末尾才释放,
# 始终有效)按行提取每个函数/方法的参数注解串缓存,注入 declare 时
# 若 tree 参数数不足,用缓存补全,保证 declare 与 define 参数数一致。
# ============================================================
VPI_PARAM_MAX: t.CDefine = 4096
VPI_PARAM_KEY_LEN: t.CDefine = 224
VPI_PARAM_LEN: t.CDefine = 1024
_g_vpi_param_keys: bytes = None
_g_vpi_param_strs: bytes = None
_g_vpi_param_count: int = 0
_g_vpi_param_cap: int = 0
# ============================================================
# _cache_vpi_return_annots_text - 从 vpi_buf 文本直接提取方法返回注解
#
# 完全不依赖 AST tree(tree 深层节点在后续处理中可能损坏),
# 直接按 vpi 生成文件的行格式提取:
# " def method(...) -> Ret: pass"(类内方法,缩进)
# "def method(...) -> Ret: pass"(模块级函数)
# "class X:"(跟踪当前类名)
# ============================================================
def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
"""从 vpi_buf 文本提取方法返回注解字符串(stdlib 持久)"""
global _g_vpi_ret_keys, _g_vpi_ret_strs, _g_vpi_ret_count, _g_vpi_ret_cap
if vpi_buf is None or src_sha1 is None:
return 0
if _g_vpi_ret_keys is None:
_g_vpi_ret_keys = stdlib.malloc(VPI_RET_MAX * VPI_KEY_LEN)
_g_vpi_ret_strs = stdlib.malloc(VPI_RET_MAX * VPI_RET_LEN)
if _g_vpi_ret_keys is None or _g_vpi_ret_strs is None:
return 0
string.memset(_g_vpi_ret_keys, 0, VPI_RET_MAX * VPI_KEY_LEN)
string.memset(_g_vpi_ret_strs, 0, VPI_RET_MAX * VPI_RET_LEN)
_g_vpi_ret_cap = VPI_RET_MAX
sha1_len: t.CSizeT = string.strlen(src_sha1)
buf_len: t.CSizeT = string.strlen(vpi_buf)
cur_cls: bytes = None
pos: t.CSizeT = 0
line_no: int = 0
while pos < buf_len:
ls: t.CSizeT = pos
le: t.CSizeT = pos
while le < buf_len and vpi_buf[le] != '\n':
le += 1
line_len: t.CSizeT = le - ls
# 跳过前导空格,检查 class/def
i: t.CSizeT = ls
while i < le and vpi_buf[i] == ' ':
i += 1
if i < le:
# class X:
if line_len >= 6 and string.strncmp(vpi_buf + i, "class ", 6) == 0:
cstart: t.CSizeT = i + 6
cend: t.CSizeT = cstart
# 类名提取:到 ':' / ' ' / '(' 前停止(继承基类括号不入类名,
# 如 "class MemBuddy(MemManager):" → "MemBuddy",与调用方短名一致)
while cend < le and vpi_buf[cend] != ':' and vpi_buf[cend] != ' ' and vpi_buf[cend] != '(':
cend += 1
if cend > cstart:
if cur_cls is not None:
stdlib.free(cur_cls)
cur_cls = stdlib.malloc(cend - cstart + 1)
if cur_cls is not None:
string.strncpy(cur_cls, vpi_buf + cstart, cend - cstart)
cur_cls[cend - cstart] = '\0'
# def name(...) -> Ret:
elif line_len >= 4 and string.strncmp(vpi_buf + i, "def ", 4) == 0:
# 模块级函数(行首无缩进):清空类名上下文,避免类结束后残留
# (如 json 的 null/int_val 等模块级函数被误存为 JsonValue.null)
if i == ls and cur_cls is not None:
stdlib.free(cur_cls)
cur_cls = None
dstart: t.CSizeT = i + 4
dend: t.CSizeT = dstart
while dend < le and vpi_buf[dend] != '(':
dend += 1
# 方法名 = [dstart, dend)
# 找 "->"
rpos: t.CSizeT = dstart
arrow_pos: t.CSizeT = le
while rpos < le - 1:
if vpi_buf[rpos] == '-' and vpi_buf[rpos + 1] == '>':
arrow_pos = rpos
break
rpos += 1
if dend > dstart:
# 返回类型:有 "-> Ret" 提取注解;无注解的非特殊方法默认 "i32"
# (与 HandlesType.infer_return_type 无 return 语句时的默认行为一致,
# 如 json._append_child / __setitem__ 无注解实际编译为 i32)。
ret_src: str = None
rtype_len: t.CSizeT = 0
if arrow_pos < le:
# 返回类型 = arrow_pos+2 跳过空格 到 ':' 前
rstart2: t.CSizeT = arrow_pos + 2
while rstart2 < le and vpi_buf[rstart2] == ' ':
rstart2 += 1
rend2: t.CSizeT = rstart2
while rend2 < le and vpi_buf[rend2] != ':':
rend2 += 1
if rend2 > rstart2:
rtype_len = rend2 - rstart2
ret_src = vpi_buf + rstart2
else:
# 无返回注解:默认 i32(特殊方法除外,见下方过滤)
rtype_len = 3
ret_src = "i32"
if ret_src is not None:
# 构造 key 和 ret_str,存入缓存
mname_len: t.CSizeT = dend - dstart
cls_len2: t.CSizeT = 0
if cur_cls is not None:
cls_len2 = string.strlen(cur_cls)
key_len: t.CSizeT = sha1_len + 1 + cls_len2 + 1 + mname_len
if key_len < VPI_KEY_LEN and rtype_len < VPI_RET_LEN:
# 方法名复制到临时缓冲(vpi_buf+dstart 非 null 结尾,不能直接 snprintf)
mname_buf: bytes = stdlib.malloc(mname_len + 1)
if mname_buf is not None:
string.strncpy(mname_buf, vpi_buf + dstart, mname_len)
mname_buf[mname_len] = '\0'
# 无注解的特殊方法(构造/析构)跳过:它们有专门调用路径
skip_def: int = 0
if arrow_pos >= le and (
string.strcmp(mname_buf, "__init__") == 0 or \
string.strcmp(mname_buf, "__new__") == 0 or \
string.strcmp(mname_buf, "__before_init__") == 0 or \
string.strcmp(mname_buf, "__after_init__") == 0):
skip_def = 1
if skip_def == 0:
key_buf: bytes = stdlib.malloc(key_len + 1)
if key_buf is not None:
if cur_cls is not None:
viperlib.snprintf(key_buf, key_len + 1, "%s.%s.%s", src_sha1, cur_cls, mname_buf)
else:
viperlib.snprintf(key_buf, key_len + 1, "%s.%s", src_sha1, mname_buf)
# 去重:同一 key 已存在则跳过(同文件可能被多次加载)
dup_key: int = 0
dki: int
for dki in range(_g_vpi_ret_count):
if string.strcmp(_g_vpi_ret_keys + dki * VPI_KEY_LEN, key_buf) == 0:
dup_key = 1
break
grow_ok: int = 1
if dup_key == 0 and _g_vpi_ret_count >= _g_vpi_ret_cap:
grow_ok = _grow_vpi_ret_cache()
if dup_key == 0 and grow_ok != 0:
string.strcpy(_g_vpi_ret_keys + _g_vpi_ret_count * VPI_KEY_LEN, key_buf)
string.strncpy(_g_vpi_ret_strs + _g_vpi_ret_count * VPI_RET_LEN,
ret_src, rtype_len)
(_g_vpi_ret_strs + _g_vpi_ret_count * VPI_RET_LEN)[rtype_len] = '\0'
_g_vpi_ret_count += 1
stdlib.free(key_buf)
stdlib.free(mname_buf)
pos = le + 1
line_no += 1
if cur_cls is not None:
stdlib.free(cur_cls)
return 1
# ============================================================
# _cache_vpi_param_annots_text - 从 vpi_buf 文本提取函数/方法参数注解串
#
# 与 _cache_vpi_return_annots_text 同构:vpi tree 深层节点在遍历中可能被
# 覆盖损坏(参数列表截断),故从原始文本按行提取每个函数/方法的参数注解,
# 以 '@' 分隔存储(含首参 self → 空串占位,保证"参数位置 == 段索引";
# 注解文法不含 '@',安全作分隔符)。
# 存储格式:key="sha1.ClassName.method"(类内)/"sha1.method"(模块级),
# val=各参数注解串 '@' 分隔(无注解的参数为空串,如 self)。
# 调用方:ParseVpiIntoModule(tree 解析后立即调用,文本始终有效)。
# ============================================================
def _cache_vpi_param_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
"""从 vpi_buf 文本提取函数/方法参数注解字符串(stdlib 持久)"""
global _g_vpi_param_keys, _g_vpi_param_strs, _g_vpi_param_count, _g_vpi_param_cap
if vpi_buf is None or src_sha1 is None:
return 0
if _g_vpi_param_keys is None:
_g_vpi_param_keys = stdlib.malloc(VPI_PARAM_MAX * VPI_PARAM_KEY_LEN)
_g_vpi_param_strs = stdlib.malloc(VPI_PARAM_MAX * VPI_PARAM_LEN)
if _g_vpi_param_keys is None or _g_vpi_param_strs is None:
return 0
string.memset(_g_vpi_param_keys, 0, VPI_PARAM_MAX * VPI_PARAM_KEY_LEN)
string.memset(_g_vpi_param_strs, 0, VPI_PARAM_MAX * VPI_PARAM_LEN)
_g_vpi_param_cap = VPI_PARAM_MAX
sha1_len: t.CSizeT = string.strlen(src_sha1)
buf_len: t.CSizeT = string.strlen(vpi_buf)
cur_cls: bytes = None
pos: t.CSizeT = 0
while pos < buf_len:
ls: t.CSizeT = pos
le: t.CSizeT = pos
while le < buf_len and vpi_buf[le] != '\n':
le += 1
line_len: t.CSizeT = le - ls
i: t.CSizeT = ls
while i < le and vpi_buf[i] == ' ':
i += 1
if i < le:
if line_len >= 6 and string.strncmp(vpi_buf + i, "class ", 6) == 0:
cstart: t.CSizeT = i + 6
cend: t.CSizeT = cstart
while cend < le and vpi_buf[cend] != ':' and vpi_buf[cend] != ' ' and vpi_buf[cend] != '(':
cend += 1
if cend > cstart:
if cur_cls is not None:
stdlib.free(cur_cls)
cur_cls = stdlib.malloc(cend - cstart + 1)
if cur_cls is not None:
string.strncpy(cur_cls, vpi_buf + cstart, cend - cstart)
cur_cls[cend - cstart] = '\0'
elif line_len >= 4 and string.strncmp(vpi_buf + i, "def ", 4) == 0:
# 模块级函数(行首无缩进):清空类名上下文
if i == ls and cur_cls is not None:
stdlib.free(cur_cls)
cur_cls = None
dstart: t.CSizeT = i + 4
dend: t.CSizeT = dstart
while dend < le and vpi_buf[dend] != '(':
dend += 1
if dend >= le:
pos = le + 1
continue
# 找匹配的 ')'(深度感知:注解可含 t.CArray[...] / tuple[...] 括号)
p: t.CSizeT = dend + 1
pdepth: int = 0
pend: t.CSizeT = dend
while p < le:
ch_p: t.CChar = vpi_buf[p]
if ch_p == '(' or ch_p == '[' or ch_p == '{':
pdepth += 1
elif ch_p == ')' or ch_p == ']' or ch_p == '}':
pdepth -= 1
if pdepth < 0:
pend = p
break
p += 1
# 逐段解析参数(顶层逗号分隔;跳过 *args/**kwargs)
plist: bytes = stdlib.malloc(VPI_PARAM_LEN)
if plist is None:
pos = le + 1
continue
plist[0] = '\0'
plist_pos: t.CSizeT = 0
seg_written: int = 0
seg_cur: t.CSizeT = dend + 1
while seg_cur < pend:
while seg_cur < pend and (vpi_buf[seg_cur] == ' ' or vpi_buf[seg_cur] == ','):
seg_cur += 1
if seg_cur >= pend:
break
s_start: t.CSizeT = seg_cur
sd: int = 0
while seg_cur < pend:
ch_s: t.CChar = vpi_buf[seg_cur]
if ch_s == '(' or ch_s == '[' or ch_s == '{':
sd += 1
elif ch_s == ')' or ch_s == ']' or ch_s == '}':
sd -= 1
elif ch_s == ',' and sd == 0:
break
seg_cur += 1
s_end: t.CSizeT = seg_cur
# 跳过 * / **(vararg/kwarg 不入 args 列表)
st: t.CSizeT = s_start
while st < s_end and vpi_buf[st] == ' ':
st += 1
if st < s_end and vpi_buf[st] == '*':
if seg_cur < pend and vpi_buf[seg_cur] == ',':
seg_cur += 1
continue
# 截断默认值(顶层 '=',如 ": str = None")
e_pos: t.CSizeT = st
ed: int = 0
while e_pos < s_end:
ch_e: t.CChar = vpi_buf[e_pos]
if ch_e == '(' or ch_e == '[' or ch_e == '{':
ed += 1
elif ch_e == ')' or ch_e == ']' or ch_e == '}':
ed -= 1
elif ch_e == '=' and ed == 0:
break
e_pos += 1
s_end = e_pos
# 提取注解:顶层 ':' 之后(无 ':' → 空串,如 self)
c_pos: t.CSizeT = st
cd2: int = 0
annot_start: t.CSizeT = s_end
while c_pos < s_end:
ch_c: t.CChar = vpi_buf[c_pos]
if ch_c == '(' or ch_c == '[' or ch_c == '{':
cd2 += 1
elif ch_c == ')' or ch_c == ']' or ch_c == '}':
cd2 -= 1
elif ch_c == ':' and cd2 == 0:
annot_start = c_pos + 1
break
c_pos += 1
a_start: t.CSizeT = annot_start
while a_start < s_end and vpi_buf[a_start] == ' ':
a_start += 1
a_end: t.CSizeT = s_end
while a_end > a_start and vpi_buf[a_end - 1] == ' ':
a_end -= 1
if plist_pos + (a_end - a_start) + 1 < VPI_PARAM_LEN:
if seg_written > 0:
plist[plist_pos] = '@'
plist_pos += 1
ai2: t.CSizeT
for ai2 in range(a_end - a_start):
plist[plist_pos] = vpi_buf[a_start + ai2]
plist_pos += 1
plist[plist_pos] = '\0'
seg_written += 1
if seg_cur < pend and vpi_buf[seg_cur] == ',':
seg_cur += 1
# 存入缓存(key = sha1.class.method / sha1.method,去重)
if plist_pos > 0 and plist[0] != '\0':
mname_len: t.CSizeT = dend - dstart
cls_len2: t.CSizeT = 0
if cur_cls is not None:
cls_len2 = string.strlen(cur_cls)
key_len: t.CSizeT = sha1_len + 1 + cls_len2 + 1 + mname_len
if key_len < VPI_PARAM_KEY_LEN:
mname_buf: bytes = stdlib.malloc(mname_len + 1)
if mname_buf is not None:
string.strncpy(mname_buf, vpi_buf + dstart, mname_len)
mname_buf[mname_len] = '\0'
key_buf: bytes = stdlib.malloc(key_len + 1)
if key_buf is not None:
if cur_cls is not None:
viperlib.snprintf(key_buf, key_len + 1, "%s.%s.%s", src_sha1, cur_cls, mname_buf)
else:
viperlib.snprintf(key_buf, key_len + 1, "%s.%s", src_sha1, mname_buf)
dup_key: int = 0
dki: int
for dki in range(_g_vpi_param_count):
if string.strcmp(_g_vpi_param_keys + dki * VPI_PARAM_KEY_LEN, key_buf) == 0:
dup_key = 1
break
grow_ok: int = 1
if dup_key == 0 and _g_vpi_param_count >= _g_vpi_param_cap:
grow_ok = _grow_vpi_param_cache()
if dup_key == 0 and grow_ok != 0:
string.strcpy(_g_vpi_param_keys + _g_vpi_param_count * VPI_PARAM_KEY_LEN, key_buf)
string.strncpy(_g_vpi_param_strs + _g_vpi_param_count * VPI_PARAM_LEN,
plist, plist_pos)
(_g_vpi_param_strs + _g_vpi_param_count * VPI_PARAM_LEN)[plist_pos] = '\0'
_g_vpi_param_count += 1
stdlib.free(key_buf)
stdlib.free(mname_buf)
stdlib.free(plist)
pos = le + 1
if cur_cls is not None:
stdlib.free(cur_cls)
return 1
# ============================================================
# _grow_vpi_param_cache - 扩容参数注解缓存(容量翻倍)
# ============================================================
def _grow_vpi_param_cache() -> int:
"""扩容参数注解缓存(容量翻倍),返回 1=成功 / 0=失败"""
global _g_vpi_param_keys, _g_vpi_param_strs, _g_vpi_param_cap
if _g_vpi_param_keys is None or _g_vpi_param_cap <= 0:
return 0
new_cap: int = _g_vpi_param_cap * 2
if new_cap > 1048576:
return 0
new_keys: bytes = stdlib.realloc(_g_vpi_param_keys, new_cap * VPI_PARAM_KEY_LEN)
if new_keys is None:
return 0
new_strs: bytes = stdlib.realloc(_g_vpi_param_strs, new_cap * VPI_PARAM_LEN)
if new_strs is None:
return 0
_g_vpi_param_keys = new_keys
_g_vpi_param_strs = new_strs
_g_vpi_param_cap = new_cap
return 1
# ============================================================
# _lookup_vpi_param_annots - 查参数注解缓存
# ============================================================
def _lookup_vpi_param_annots(src_sha1: str, cls_name: str, method_name: str) -> str:
"""查参数注解缓存;返回注解串('@' 分隔,含首参空串占位)或 None"""
if src_sha1 is None or method_name is None or _g_vpi_param_keys is None:
return None
sha1_len: t.CSizeT = string.strlen(src_sha1)
mlen: t.CSizeT = string.strlen(method_name)
clen: t.CSizeT = 0
if cls_name is not None:
clen = string.strlen(cls_name)
key_len: t.CSizeT = sha1_len + clen + mlen + 2
if key_len >= VPI_PARAM_KEY_LEN:
return None
key_buf: bytes = stdlib.malloc(key_len + 1)
if key_buf is None:
return None
if cls_name is not None:
viperlib.snprintf(key_buf, key_len + 1, "%s.%s.%s", src_sha1, cls_name, method_name)
else:
viperlib.snprintf(key_buf, key_len + 1, "%s.%s", src_sha1, method_name)
r: str = None
ki: int
for ki in range(_g_vpi_param_count):
if string.strcmp(_g_vpi_param_keys + ki * VPI_PARAM_KEY_LEN, key_buf) == 0:
r = _g_vpi_param_strs + ki * VPI_PARAM_LEN
break
stdlib.free(key_buf)
return r
# ============================================================
# _repair_declare_params_from_text - 用文本缓存补全 declare 参数
#
# vpi tree 参数列表被覆盖损坏时(param_count 偏小,尾部默认参数丢失),
# 用 _cache_vpi_param_annots_text 的文本缓存补全 declare:参数位置 p
# 对应缓存段 p(含首参空串占位)。补全后 declare 参数数与 define 一致,
# 调用点默认参数填充才能生效,避免被调函数读栈垃圾。
# ============================================================
def _repair_declare_params_from_text(pool: memhub.MemBuddy | t.CPtr,
func: llvmlite.Function | t.CPtr,
src_sha1: str,
cls_name: str,
method_name: str,
param_count: int,
i32_ty: llvmlite.LLVMType | t.CPtr,
i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> int:
"""用 vpi 文本缓存的参数注解补全 declare 参数;返回补全后参数数"""
if func is None or src_sha1 is None or method_name is None:
return param_count
prm_annots: str = _lookup_vpi_param_annots(src_sha1, cls_name, method_name)
if prm_annots is None or prm_annots[0] == '\0':
return param_count
# 统计文本参数数('@' 分隔;段数 = 分隔符数 + 1,含首参)
pa_count: int = 1
pi2: t.CSizeT = 0
while prm_annots[pi2] != '\0':
if prm_annots[pi2] == '@':
pa_count += 1
pi2 += 1
if pa_count <= param_count:
return param_count
# 补全缺失参数:段索引 >= param_count 的段即缺失参数位置
seg_idx: int = 0
seg_start2: t.CSizeT = 0
cur2: t.CSizeT = 0
while prm_annots[cur2] != '\0':
while prm_annots[cur2] != '\0' and prm_annots[cur2] != '@':
cur2 += 1
seg_len2: t.CSizeT = cur2 - seg_start2
if prm_annots[cur2] == '@':
cur2 += 1
if seg_idx >= param_count:
param_ty_r: llvmlite.LLVMType | t.CPtr = i8_ptr_ty
if seg_len2 > 0:
seg_buf_r: bytes = stdlib.malloc(seg_len2 + 1)
if seg_buf_r is not None:
string.strncpy(seg_buf_r, prm_annots + seg_start2, seg_len2)
seg_buf_r[seg_len2] = '\0'
rt: llvmlite.LLVMType | t.CPtr = _resolve_vpi_ret_annot_str(
pool, seg_buf_r, i32_ty, i8_ptr_ty)
if rt is not None:
param_ty_r = rt
stdlib.free(seg_buf_r)
llvmlite.add_param(pool, func, param_ty_r, "arg")
seg_start2 = cur2
seg_idx += 1
return pa_count
# ============================================================
# _resolve_vpi_param_annot_seg - 按段索引解析 vpi 参数注解为 LLVMType
#
# 返回类型已用字符串注解缓存解析(绕开 vpi tree 深层节点 kind() 损坏,见
# _inject_method_declare L1673-1675 注释);参数此前仍走 AST
# resolve_annotation_type,同样会得垃圾类型(如 t.CSizeT → i32),跨模块
# declare 参数类型错 → 调用侧 trunc i64→i32 传参、callee 按 i64 读栈残留
# 垃圾高位 → can=巨值读未写槽位崩溃(SlotsBugTest 根因)。
# 此处与返回类型同源:从 @-joined 注解串取第 seg_idx 段(0=self 空段),
# 用 _resolve_vpi_ret_annot_str 字符串解析。空段/未命中返回 None(走 AST 兜底)。
# ============================================================
def _resolve_vpi_param_annot_seg(pool: memhub.MemBuddy | t.CPtr,
src_sha1: str,
cls_name: str,
method_name: str,
seg_idx: int,
i32_ty: llvmlite.LLVMType | t.CPtr,
i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""按段索引解析 vpi 参数注解段为 LLVMType,空段/失败返回 None"""
if pool is None or src_sha1 is None or method_name is None:
return None
prm_annots_p: str = _lookup_vpi_param_annots(src_sha1, cls_name, method_name)
if prm_annots_p is None or prm_annots_p[0] == '\0':
return None
cur_p: t.CSizeT = 0
cur_seg_p: int = 0
while cur_seg_p < seg_idx and prm_annots_p[cur_p] != '\0':
while prm_annots_p[cur_p] != '\0' and prm_annots_p[cur_p] != '@':
cur_p += 1
if prm_annots_p[cur_p] == '@':
cur_p += 1
cur_seg_p += 1
seg_start_p: t.CSizeT = cur_p
seg_len_p: t.CSizeT = 0
while prm_annots_p[seg_start_p + seg_len_p] != '\0' \
and prm_annots_p[seg_start_p + seg_len_p] != '@':
seg_len_p += 1
if seg_len_p == 0:
return None
seg_buf_p: bytes = stdlib.malloc(seg_len_p + 1)
if seg_buf_p is None:
return None
string.strncpy(seg_buf_p, prm_annots_p + seg_start_p, seg_len_p)
seg_buf_p[seg_len_p] = '\0'
seg_ty_p: llvmlite.LLVMType | t.CPtr = _resolve_vpi_ret_annot_str(
pool, seg_buf_p, i32_ty, i8_ptr_ty)
stdlib.free(seg_buf_p)
return seg_ty_p
# ============================================================
# _grow_vpi_ret_cache - 扩容方法返回注解缓存(容量翻倍)
#
# 背景:VPI_RET_MAX 初始 4096,但 includes 模块量大(~90 个 stub、
# 每个几十个方法),缓存写满后条目被静默丢弃,导致已缓存模块的
# 方法(如 JsonValue._pool_alloc)查不到而 fail-fast 误报。
# 用 stdlib.realloc 翻倍扩容(上限 1M 条目,防御异常增长)。
# ============================================================
def _grow_vpi_ret_cache() -> int:
"""扩容返回注解缓存(容量翻倍),返回 1=成功 / 0=失败"""
global _g_vpi_ret_keys, _g_vpi_ret_strs, _g_vpi_ret_cap
if _g_vpi_ret_keys is None or _g_vpi_ret_cap <= 0:
return 0
new_cap: int = _g_vpi_ret_cap * 2
if new_cap > 1048576:
return 0
new_keys: bytes = stdlib.realloc(_g_vpi_ret_keys, new_cap * VPI_KEY_LEN)
if new_keys is None:
return 0
new_strs: bytes = stdlib.realloc(_g_vpi_ret_strs, new_cap * VPI_RET_LEN)
if new_strs is None:
return 0
_g_vpi_ret_keys = new_keys
_g_vpi_ret_strs = new_strs
_g_vpi_ret_cap = new_cap
return 1
# ============================================================
# _ensure_vpi_ret_annot_loaded - 按需加载模块的 vpi stub 返回注解缓存
#
# 背景:_cache_vpi_return_annots_text 仅在 stub 注入(LoadDepVpiIntoModule,
# 即"当前模块 import 了该模块")时填充缓存。但模块自身翻译时,自己的
# stub 尚未被任何依赖方读取,且自己的 text.ll 要翻译完成才落盘,导致
# "同模块方法调用(定义在后)"(如 json.__setitem__ 调用后定义的
# _pool_alloc)在 found_func/text.ll/vpi 缓存三类来源上全部未命中。
# 此函数在缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi
# (Converter 在 deps 阶段已生成全部 stub 文件),填充缓存后即可命中。
# 幂等:模块已缓存过则跳过,不重复读文件。
# ============================================================
def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int:
"""按需加载模块 vpi stub 的返回注解缓存,返回 1=已就绪 / 0=失败"""
if src_sha1 is None or temp_dir is None:
return 0
sha1_len: t.CSizeT = string.strlen(src_sha1)
if sha1_len < 2:
return 0
# 该模块已有缓存条目 → 跳过(幂等)
if _g_vpi_ret_keys is not None and _g_vpi_ret_count > 0:
pre_i: int
for pre_i in range(_g_vpi_ret_count):
pre_key: bytes = _g_vpi_ret_keys + pre_i * VPI_KEY_LEN
if string.strncmp(pre_key, src_sha1, sha1_len) == 0 and pre_key[sha1_len] == '.':
return 1
# 构造路径 {temp_dir}/{sha1[0:2]}/{sha1}.vpi(逐字符拼接,避免 %c 格式化问题)
td_len: t.CSizeT = string.strlen(temp_dir)
path_len: t.CSizeT = td_len + sha1_len + 12
vpi_path: bytes = stdlib.malloc(path_len)
if vpi_path is None:
return 0
string.strcpy(vpi_path, temp_dir)
vpi_path[td_len] = '/'
vpi_path[td_len + 1] = src_sha1[0]
vpi_path[td_len + 2] = src_sha1[1]
vpi_path[td_len + 3] = '/'
string.strcpy(vpi_path + td_len + 4, src_sha1)
string.strcpy(vpi_path + td_len + 4 + sha1_len, ".vpi")
f: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R)
if f.closed:
stdlib.free(vpi_path)
return 0
vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE)
if vpi_buf is None:
f.close()
stdlib.free(vpi_path)
return 0
bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE)
f.close()
if bytes_read > 0:
if bytes_read < VPI_READ_BUF_SIZE:
vpi_buf[bytes_read] = 0
else:
vpi_buf[VPI_READ_BUF_SIZE - 1] = 0
_cache_vpi_return_annots_text(vpi_buf, src_sha1)
stdlib.free(vpi_buf)
stdlib.free(vpi_path)
return 1
# ============================================================
# _ensure_vpi_param_annot_loaded - 按需加载模块的 vpi stub 参数注解缓存
#
# 与 _ensure_vpi_ret_annot_loaded 同构:_cache_vpi_param_annots_text 仅在
# stub 注入(LoadDepVpiIntoModule,即"当前模块 import 了该模块")时填充。
# 但跨模块方法调用可能绕过 stub 注入——如 ExprGenAttr 经 Generator 属性
# (self.Gen.ExprGenCdef.method)调用依赖模块方法,依赖收集不加载其 vpi
# → 函数表无 ParamCount/默认参数 → 调用点缺参 → 被调函数读栈残留
# (IterTest 跨模块 CDefine 查找 depth=160 假递归根因)。
# 此函数在参数缓存未命中时直接读 vpi 文件填充,幂等跳过。
# ============================================================
def _ensure_vpi_param_annot_loaded(src_sha1: str, temp_dir: str) -> int:
"""按需加载模块 vpi stub 的参数注解缓存,返回 1=已就绪 / 0=失败"""
if src_sha1 is None or temp_dir is None:
return 0
sha1_len: t.CSizeT = string.strlen(src_sha1)
if sha1_len < 2:
return 0
# 该模块已有参数缓存条目 → 跳过(幂等)
if _g_vpi_param_keys is not None and _g_vpi_param_count > 0:
pre_i: int
for pre_i in range(_g_vpi_param_count):
pre_key: bytes = _g_vpi_param_keys + pre_i * VPI_PARAM_KEY_LEN
if string.strncmp(pre_key, src_sha1, sha1_len) == 0 and pre_key[sha1_len] == '.':
return 1
# 构造路径 {temp_dir}/{sha1[0:2]}/{sha1}.vpi(逐字符拼接,避免 %c 格式化问题)
td_len: t.CSizeT = string.strlen(temp_dir)
path_len: t.CSizeT = td_len + sha1_len + 12
vpi_path: bytes = stdlib.malloc(path_len)
if vpi_path is None:
return 0
string.strcpy(vpi_path, temp_dir)
vpi_path[td_len] = '/'
vpi_path[td_len + 1] = src_sha1[0]
vpi_path[td_len + 2] = src_sha1[1]
vpi_path[td_len + 3] = '/'
string.strcpy(vpi_path + td_len + 4, src_sha1)
string.strcpy(vpi_path + td_len + 4 + sha1_len, ".vpi")
f: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R)
if f.closed:
stdlib.free(vpi_path)
return 0
vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE)
if vpi_buf is None:
f.close()
stdlib.free(vpi_path)
return 0
bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE)
f.close()
if bytes_read > 0:
if bytes_read < VPI_READ_BUF_SIZE:
vpi_buf[bytes_read] = 0
else:
vpi_buf[VPI_READ_BUF_SIZE - 1] = 0
_cache_vpi_param_annots_text(vpi_buf, src_sha1)
stdlib.free(vpi_buf)
stdlib.free(vpi_path)
return 1
# ============================================================
# _subst_template_arg - 替换注解字符串中独立的模板参数 token
#
# 泛型模板返回注解(如 list[T].get -> T)中的类型参数名(T)
# 需替换为实际特化实参(AST|t.CPtr)后才能解析。仅替换前后
# 非字母数字下划线边界的独立 token(避免误替换 t.CSizeT 等)。
# 返回 stdlib 新分配字符串(调用者负责 free);失败返回 None。
# ============================================================
def _subst_template_arg(annot: str, tparam: str, targ: str) -> str:
"""替换 annot 中独立的 tparam token 为 targ"""
if annot is None or tparam is None or targ is None:
return None
alen: t.CSizeT = string.strlen(annot)
tplen: t.CSizeT = string.strlen(tparam)
tglen: t.CSizeT = string.strlen(targ)
if tplen == 0 or tplen > alen:
return annot
# 容量 = alen + tglen*8 + 16 只覆盖 ≤8 次替换;替换次数无上限时
# opos 会越界写穿 stdlib 堆(哨兵盲区 → CRT 堆损坏后任意 malloc/free
# 静默段错误、崩溃点漂移)。按最坏上界分配:每替换消耗 tplen 写 tglen,
# 最坏全部被替换 → alen + (alen/tplen)*tglen + 1。
max_sub: t.CSizeT = alen / tplen
if max_sub == 0:
max_sub = 1
out_size: t.CSizeT = alen + max_sub * tglen + 1
out: bytes = stdlib.malloc(out_size)
if out is None:
return None
opos: t.CSizeT = 0
p: t.CSizeT = 0
while p < alen:
# 防御:opos 超分配即 fail-fast 返回 None(绝不越界写)
if opos >= out_size:
stdlib.free(out)
return None
matched: int = 0
if annot[p] == tparam[0] and p + tplen <= alen:
if string.strncmp(annot + p, tparam, tplen) == 0:
prev_ch: int = 0
next_ch: int = 0
if p > 0:
prev_ch = annot[p - 1]
if p + tplen < alen:
next_ch = annot[p + tplen]
prev_ok: int = 1
if (prev_ch >= 'a' and prev_ch <= 'z') or (prev_ch >= 'A' and prev_ch <= 'Z') or \
(prev_ch >= '0' and prev_ch <= '9') or prev_ch == '_':
prev_ok = 0
next_ok: int = 1
if (next_ch >= 'a' and next_ch <= 'z') or (next_ch >= 'A' and next_ch <= 'Z') or \
(next_ch >= '0' and next_ch <= '9') or next_ch == '_':
next_ok = 0
if prev_ok != 0 and next_ok != 0:
string.strncpy(out + opos, targ, tglen)
opos += tglen
p += tplen
matched = 1
if matched == 0:
out[opos] = annot[p]
opos += 1
p += 1
out[opos] = '\0'
return out
# ============================================================
# _lookup_vpi_ret_annot - 查找缓存的方法返回注解字符串
# ============================================================
def _lookup_vpi_ret_annot(src_sha1: str, cls_name: str, method_name: str) -> str:
"""查找缓存的方法返回注解字符串,未找到返回 None"""
if _g_vpi_ret_keys is None or _g_vpi_ret_count == 0:
return None
if src_sha1 is None or method_name is None:
return None
sha1_len: t.CSizeT = string.strlen(src_sha1)
cl_len: t.CSizeT = 0
if cls_name is not None:
cl_len = string.strlen(cls_name)
me_len: t.CSizeT = string.strlen(method_name)
key_len: t.CSizeT = sha1_len + 1 + cl_len + 1 + me_len
key_buf: bytes = stdlib.malloc(key_len + 1)
if key_buf is None:
return None
if cls_name is not None:
viperlib.snprintf(key_buf, key_len + 1, "%s.%s.%s", src_sha1, cls_name, method_name)
else:
viperlib.snprintf(key_buf, key_len + 1, "%s.%s", src_sha1, method_name)
i: int
for i in range(_g_vpi_ret_count):
if string.strcmp(_g_vpi_ret_keys + i * VPI_KEY_LEN, key_buf) == 0:
stdlib.free(key_buf)
return _g_vpi_ret_strs + i * VPI_RET_LEN
# 泛型特化归一化匹配:list[AST|t.CPtr].get → list[T].get 模板缓存。
# 命中后把模板注解中的类型参数名(如 T)替换为实参(AST|t.CPtr)再返回。
# 不依赖模板类已注册/特化已发生(base.vp 可能先于 list 模块翻译)。
if cls_name is not None:
lbkt_v: str = string.strchr(cls_name, '[')
if lbkt_v is not None:
rbkt_v: str = string.strrchr(cls_name, ']')
if rbkt_v is not None and rbkt_v > lbkt_v:
base_len_v: t.CSizeT = t.CSizeT(t.CUInt64T(lbkt_v) - t.CUInt64T(cls_name))
param_len_v: t.CSizeT = t.CSizeT(t.CUInt64T(rbkt_v) - t.CUInt64T(lbkt_v) - 1)
if base_len_v > 0 and param_len_v > 0:
base_v: str = stdlib.malloc(base_len_v + 1)
param_v: str = stdlib.malloc(param_len_v + 1)
if base_v is not None and param_v is not None:
string.strncpy(base_v, cls_name, base_len_v)
base_v[base_len_v] = '\0'
string.strncpy(param_v, lbkt_v + 1, param_len_v)
param_v[param_len_v] = '\0'
# 遍历缓存找 {sha1}.{base}[xxx].{method}
gi: int
for gi in range(_g_vpi_ret_count):
gk: bytes = _g_vpi_ret_keys + gi * VPI_KEY_LEN
if string.strncmp(gk, src_sha1, sha1_len) == 0 and \
gk[sha1_len] == '.' and \
string.strncmp(gk + sha1_len + 1, base_v, base_len_v) == 0 and \
gk[sha1_len + 1 + base_len_v] == '[':
gklb: str = string.strchr(gk + sha1_len + 1 + base_len_v, ']')
if gklb is not None:
tparam_len_v: t.CSizeT = t.CSizeT(
t.CUInt64T(gklb) - t.CUInt64T(gk + sha1_len + 1 + base_len_v) - 1)
meth_start_v: str = gklb + 2 # 跳过 ']' 和 '.'
if tparam_len_v > 0 and string.strcmp(meth_start_v, method_name) == 0:
tparam_v: str = stdlib.malloc(tparam_len_v + 1)
if tparam_v is not None:
string.strncpy(tparam_v, gk + sha1_len + 1 + base_len_v + 1, tparam_len_v)
tparam_v[tparam_len_v] = '\0'
new_annot_v: str = _subst_template_arg(
_g_vpi_ret_strs + gi * VPI_RET_LEN, tparam_v, param_v)
stdlib.free(tparam_v)
stdlib.free(base_v)
stdlib.free(param_v)
stdlib.free(key_buf)
return new_annot_v
stdlib.free(base_v)
stdlib.free(param_v)
stdlib.free(key_buf)
return None
# ============================================================
# _resolve_single_vpi_type_str - 解析单个类型段(无 CPtr/CVoid)为 LLVMType
#
# 纯字符串解析,不构造 AST 节点。
# ============================================================
def _resolve_single_vpi_type_str(pool: memhub.MemBuddy | t.CPtr,
seg: str) -> llvmlite.LLVMType | t.CPtr:
"""解析单个类型段为 LLVMType,无法识别返回 None"""
if seg is None or pool is None:
return None
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 内置别名
if string.strcmp(seg, "str") == 0 or string.strcmp(seg, "bytes") == 0:
return i8_ptr_ty
if string.strcmp(seg, "int") == 0:
return llvmlite.Int32(pool)
if string.strcmp(seg, "bool") == 0:
return llvmlite.Int1(pool)
# LLVM 类型串:vpi 缓存对无返回注解方法默认存 "i32"
# (见 _cache_vpi_return_annots_text;push 等无注解方法返回 i32)。
# 之前缺分支导致解析返回 None → 泛型模板方法(Vector[int].push)调用
# 走 fail-fast"方法返回类型无法精确获取",实际是约定不一致。
if string.strcmp(seg, "i32") == 0:
return llvmlite.Int32(pool)
if string.strcmp(seg, "i64") == 0:
return llvmlite.Int64(pool)
if string.strcmp(seg, "i16") == 0:
return llvmlite.Int16(pool)
if string.strcmp(seg, "i8") == 0:
return i8_ty
if string.strcmp(seg, "i1") == 0:
return llvmlite.Int1(pool)
if string.strcmp(seg, "f32") == 0:
return llvmlite.Float(pool)
if string.strcmp(seg, "f64") == 0:
return llvmlite.Double(pool)
if string.strcmp(seg, "void") == 0:
return llvmlite.Void(pool)
# vpi 源文本中 `-> None` 被原样缓存为 "None"(_cache_vpi_return_annots_text
# 提取 arrow_pos 后到 ':' 前的内容)。此前无 "None" 分支 → 解析返回 None →
# `-> None` 的跨模块方法(如 AssignGenMixin._register_cdefine_from_value)vpi
# 缓存命中也解析不出 void → 方法返回类型 fail-fast 误报(追加80e 断点 B)。
# None 与 void 同义,归一到 Void。
if string.strcmp(seg, "None") == 0:
return llvmlite.Void(pool)
# t.XXX / c.XXX 内置类型映射
if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0:
attr_nm: str = seg + 2
# t.CExport:导出标记,非真实类型。定义侧 FuncGenDef 已特判
# CExport → Void(返回空),vpi 返回注解缓存原样缓存 "t.CExport",
# 调用方解析必须同样归一 Void,否则 vpi 缓存 miss → 跨模块函数
# 返回类型无法精确获取 fail-fast 误报(VKernel mm.free -> t.CExport
# "1fdad1c76fb116bc.free 返回类型无法精确获取" 根因)。
if string.strcmp(attr_nm, "CExport") == 0:
return llvmlite.Void(pool)
mapped_ty: llvmlite.LLVMType | t.CPtr = GenApi.map_t_type(pool, attr_nm)
if mapped_ty is not None:
return mapped_ty
return None
# 模块限定类名 "memhub.MemBuddy" → 取最后一段查 struct
last_dot: str = string.strrchr(seg, '.')
if last_dot is not None:
cls_nm2: str = last_dot + 1
mod_struct_ty: llvmlite.LLVMType | t.CPtr = GenApi.get_struct_type(cls_nm2)
if mod_struct_ty is not None:
return mod_struct_ty
# 枚举兜底:t.CEnum 注册在独立 EnumTable(EnumEntry),不进 StructTable,
# get_struct_type 查不到。如 fat32.close -> types.FRESULT(FRESULT 是枚举),
# struct 落空必须回退枚举表取 BaseTy(通常 i32)。否则 vpi 返回注解解析
# 失败 → 跨模块函数返回类型无法精确获取 → text.ll 缺失(依赖环)时
# 永久 pending(VKernel fat32→sched→process→fat32 Phase A 死锁根因)。
mod_enum_ty: llvmlite.LLVMType | t.CPtr = GenApi.get_enum_base_type(cls_nm2)
if mod_enum_ty is not None:
return mod_enum_ty
return None
# 裸类名 → 全局 struct 注册表
bare_struct_ty: llvmlite.LLVMType | t.CPtr = GenApi.get_struct_type(seg)
if bare_struct_ty is not None:
return bare_struct_ty
# 裸枚举名 → EnumTable 兜底(同上:枚举不在 StructTable)
bare_enum_ty: llvmlite.LLVMType | t.CPtr = GenApi.get_enum_base_type(seg)
if bare_enum_ty is not None:
return bare_enum_ty
# 裸 typedef 名(如 VOIDPTR)
mapped_bare: llvmlite.LLVMType | t.CPtr = GenApi.map_t_type(pool, seg)
if mapped_bare is not None:
return mapped_bare
return None
# ============================================================
# _resolve_vpi_ret_annot_str - 直接解析 vpi 返回注解字符串为 LLVMType
#
# 背景:_parse_type_string 跨模块构造 ast.Name/ast.BinOp 在 TPV 中可能
# 退化为裸函数调用(__init__/vtable 未执行),节点 kind() 异常导致
# resolve 得到垃圾类型(如 Ptr(i8))。此处纯字符串解析避免该问题。
#
# 支持: "str"/"bytes" → i8*; "int" → i32; "bool" → i1;
# "t.CChar" 等 → map_t_type 映射;
# "X | t.CPtr" → Ptr(Struct X); "t.CVoid | t.CPtr" → i8*;
# "memhub.MemBuddy | t.CPtr" → Ptr(Struct MemBuddy)。
# ============================================================
def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy | t.CPtr,
ret_annot: str,
i32_ty: llvmlite.LLVMType | t.CPtr,
i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""直接解析 vpi 返回注解字符串为 LLVMType(不构造 AST 节点)"""
if ret_annot is None or pool is None:
return None
# tuple[T1, T2, ...] → 匿名结构体 {T1, T2, ...}(多返回值打包,
# 与 TypeGenMixin._resolve_generic_subscript_ty 的 tuple 分支一致)。
# b2a_hqx -> tuple[str, t.CUnsignedShort] 之前按普通段解析失败 → 调用侧
# 返回类型退化 i8* → tuple 解包失效(hqx 段崩溃根因)。
if string.strncmp(ret_annot, "tuple[", 6) == 0:
inner_len_t: t.CSizeT = 0
while ret_annot[6 + inner_len_t] != ']' and ret_annot[6 + inner_len_t] != '\0':
inner_len_t += 1
if ret_annot[6 + inner_len_t] != ']' or inner_len_t == 0:
return None
tup_head_t: llvmlite.ParamNode | t.CPtr = None
tup_tail_t: llvmlite.ParamNode | t.CPtr = None
tup_count_t: int = 0
p_t: t.CSizeT = 0
while p_t < inner_len_t:
# 跳过前导空格/逗号
while p_t < inner_len_t and (ret_annot[6 + p_t] == ' ' or ret_annot[6 + p_t] == ','):
p_t += 1
seg_s_t: t.CSizeT = p_t
while p_t < inner_len_t and ret_annot[6 + p_t] != ',':
p_t += 1
seg_e_t: t.CSizeT = p_t
while seg_e_t > seg_s_t and ret_annot[6 + seg_e_t - 1] == ' ':
seg_e_t -= 1
if seg_e_t > seg_s_t:
seg_len_t: t.CSizeT = seg_e_t - seg_s_t
seg_buf_t: bytes = stdlib.malloc(seg_len_t + 1)
if seg_buf_t is None:
return None
string.strncpy(seg_buf_t, ret_annot + 6 + seg_s_t, seg_len_t)
seg_buf_t[seg_len_t] = '\0'
seg_ty_t: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(pool, seg_buf_t)
stdlib.free(seg_buf_t)
if seg_ty_t is None:
return None
tup_node_t: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, seg_ty_t)
if tup_node_t is None:
return None
if tup_head_t is None:
tup_head_t = tup_node_t
else:
llvmlite.paramnode_set_next(tup_tail_t, tup_node_t)
tup_tail_t = tup_node_t
tup_count_t += 1
if tup_head_t is None:
return None
return llvmlite.Struct(pool, tup_head_t, tup_count_t, None)
has_cptr: int = 0
has_cvoid: int = 0
first_ty: llvmlite.LLVMType | t.CPtr = None
buf_len: t.CSizeT = string.strlen(ret_annot)
pos: t.CSizeT = 0
while pos <= buf_len:
# 跳过前导空格
while pos < buf_len and ret_annot[pos] == ' ':
pos += 1
seg_start: t.CSizeT = pos
while pos < buf_len and ret_annot[pos] != '|':
pos += 1
seg_end: t.CSizeT = pos
# trim 尾随空格
while seg_end > seg_start and ret_annot[seg_end - 1] == ' ':
seg_end -= 1
if seg_end > seg_start:
seg_len2: t.CSizeT = seg_end - seg_start
seg_buf: bytes = stdlib.malloc(seg_len2 + 1)
if seg_buf is None:
break
string.strncpy(seg_buf, ret_annot + seg_start, seg_len2)
seg_buf[seg_len2] = '\0'
if string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0:
has_cptr = 1
elif string.strcmp(seg_buf, "t.CVoid") == 0 or string.strcmp(seg_buf, "CVoid") == 0:
has_cvoid = 1
else:
seg_ty: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(pool, seg_buf)
if seg_ty is not None and first_ty is None:
first_ty = seg_ty
stdlib.free(seg_buf)
pos += 1 # 跳过 '|'
if has_cptr != 0:
if has_cvoid != 0:
return i8_ptr_ty
if first_ty is not None:
return llvmlite.Ptr(pool, first_ty)
return i8_ptr_ty
if has_cvoid != 0:
# t.CVoid | t.State:State 是装饰器标记(编译侧忽略),t.CVoid → void。
# 此前缺此分支 → first_ty=None → 返回 None → 跨模块函数返回类型
# fail-fast(CPythonTest Py_Initialize() -> t.CVoid | t.State 根因)。
return llvmlite.Void(pool)
if first_ty is not None:
return first_ty
return None
# ============================================================
# VpiReader - .vpi AST 解析器
#
# 从 .vpi 存根文件读取函数签名和 CTypedef 定义,注入 declare
# 到当前模块。用于跨模块函数调用时补充缺失的函数声明。
#
# .vpi 文件由 StubGen.Converter._GeneratePyiFromAst 生成,
# 包含函数签名(无函数体)、类定义、变量声明、import 语句。
# VpiReader 解析 .vpi 为 AST,提取 FunctionDef 和 CTypedef
# AnnAssign,在当前模块创建对应的 declare 声明。
# ============================================================
# .vpi 读取缓冲区大小(256KB,与 PYI_BUF_SIZE 一致)
VPI_READ_BUF_SIZE: t.CSizeT = 262144
# SHA1 字符串长度(16 hex 字符)
SHA1_LEN: t.CSizeT = 16
# ============================================================
# _extract_sha1_from_path - 从 .vpi 文件路径提取 SHA1
#
# .vpi 文件名格式为 {sha1}.vpi,sha1 是 16 位十六进制字符。
# 从路径中找到最后一个路径分隔符(/ 或 \),取后续文件名的前
# 16 个字符作为 SHA1。分配新缓冲区存储 SHA1(调用者负责 free)。
#
# Args:
# vpi_path: .vpi 文件路径
#
# Returns:
# SHA1 字符串(stdlib.malloc 分配,需 stdlib.free),None 失败
# ============================================================
def _extract_sha1_from_path(vpi_path: str) -> str:
"""从 .vpi 文件路径提取 SHA1(文件名前16字符,malloc 分配)"""
if vpi_path is None:
return None
# 查找最后一个路径分隔符 / 或 \
total_len: t.CSizeT = string.strlen(vpi_path)
if total_len < SHA1_LEN + 4: # 至少需要 "xxxx.vpi" 的长度
return None
# 从后向前查找最后一个 / 或 \
sep_pos: t.CSizeT = total_len
while sep_pos > 0:
sep_pos -= 1
ch: t.CChar = vpi_path[sep_pos]
if ch == '/' or ch == '\\':
sep_pos += 1
break
if sep_pos + SHA1_LEN > total_len:
return None
# 分配缓冲区并复制 SHA1(16 字符 + null 终止符)
sha1_buf: str = stdlib.malloc(SHA1_LEN + 1)
if sha1_buf is None:
return None
string.strncpy(sha1_buf, vpi_path + sep_pos, SHA1_LEN)
sha1_buf[SHA1_LEN] = '\0'
return sha1_buf
# ============================================================
# _stub_add_imported - 追加模块名到空格分隔列表(stub 上下文收集用)
#
# 与 HandlesImports.add_imported_module 逻辑一致,但内联在本模块,
# 避免 VpiReader → HandlesImports → StubMerger → VpiReader 循环依赖。
# ============================================================
def _stub_add_imported(pool: memhub.MemBuddy | t.CPtr,
cur: str, name: str) -> str:
"""追加模块名到空格分隔列表(pool 分配),返回新列表"""
if name is None:
return cur
if cur is None:
nlen: t.CSizeT = string.strlen(name)
buf: str = pool.alloc(nlen + 1)
if buf is not None:
string.strcpy(buf, name)
return buf
old_len: t.CSizeT = string.strlen(cur)
name_len: t.CSizeT = string.strlen(name)
buf2: str = pool.alloc(old_len + 1 + name_len + 1)
if buf2 is not None:
string.strcpy(buf2, cur)
buf2[old_len] = ' '
string.strcpy(buf2 + old_len + 1, name)
return buf2
# ============================================================
# _stub_add_from - 追加 from-import 映射("local:mod[:orig]" 空格分隔)
# ============================================================
def _stub_add_from(pool: memhub.MemBuddy | t.CPtr,
cur: str, local: str, mod: str, orig: str) -> str:
"""追加 from-import 映射(pool 分配),返回新列表"""
if local is None or mod is None:
return cur
entry: str = pool.alloc(256)
if entry is None:
return cur
if orig is not None and string.strcmp(orig, local) != 0:
viperlib.snprintf(entry, 256, "%s:%s:%s", local, mod, orig)
else:
viperlib.snprintf(entry, 256, "%s:%s", local, mod)
if cur is None:
return entry
old_len: t.CSizeT = string.strlen(cur)
entry_len: t.CSizeT = string.strlen(entry)
buf: str = pool.alloc(old_len + 1 + entry_len + 1)
if buf is not None:
string.strcpy(buf, cur)
buf[old_len] = ' '
string.strcpy(buf + old_len + 1, entry)
return buf
# ============================================================
# ParseVpiIntoModule - 解析 .vpi 文件,将函数声明注入到当前模块
#
# 读取 .vpi 文件 → AST 解析 → 遍历顶层节点 → 创建 declare 声明。
# 对每个 FunctionDef:
# 1. 提取函数名、参数、返回类型
# 2. 检测 CExport/CExtern/State 标记(决定是否用裸名)
# 3. 用 find_func_in_module 去重(已存在则跳过)
# 4. 用 create_declare + add_param 创建 declare 声明
# 5. 注册到函数表(用裸名,非 mangled_name)
# 对 AnnAssign with t.CTypedef:调用 register_ctypedef 注册
# ClassDef/Import/ImportFrom:跳过
#
# Args:
# pool: 内存池
# mod: 当前 LLVM 模块
# vpi_path: .vpi 文件路径
# trans: 翻译器对象
#
# Returns:
# 1=成功,0=失败
# ============================================================
def ParseVpiIntoModule(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
vpi_path: str,
trans: HT.Translator | t.CPtr) -> int:
"""解析 .vpi 文件,将函数声明注入到当前模块
解析 stub 期间临时将错误归因切换到被读 stub 文件(.vpi):
注解解析失败时 fatal_error 应指向 stub 及其行号(真实出错位置),
而非当前编译模块(错误归因修复)。
"""
if pool is None or mod is None or vpi_path is None or trans is None:
return 0
saved_cur: str = GenApi.get_current_file()
GenApi.set_current_file_type(vpi_path)
ret: int = _ParseVpiIntoModuleImpl(pool, mod, vpi_path, trans)
GenApi.set_current_file_type(saved_cur)
return ret
def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
vpi_path: str,
trans: HT.Translator | t.CPtr) -> int:
"""解析 .vpi 文件,将函数声明注入到当前模块(ParseVpiIntoModule 实际实现)"""
if pool is None or mod is None or vpi_path is None or trans is None:
return 0
# 1. 读取 .vpi 文件
f: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R)
if f.closed:
return 0
vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE)
if vpi_buf is None:
f.close()
return 0
bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE)
f.close()
if bytes_read <= 0:
stdlib.free(vpi_buf)
return 0
if bytes_read < VPI_READ_BUF_SIZE:
vpi_buf[bytes_read] = 0
else:
vpi_buf[VPI_READ_BUF_SIZE - 1] = 0
# 2. 解析为 AST
lx: ast.Lexer | t.CPtr = ast.new_lexer(pool)
if lx is None:
stdlib.free(vpi_buf)
return 0
ast._lexer_init(lx, vpi_buf, pool)
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
tree: ast.AST | t.CPtr = ast.parse_tokens(pool, tokens)
# vpi_buf 延迟释放:AST 字符串字段可能引用 vpi_buf 内部(token 零拷贝),
# 提前 free 会导致 Name.id/Attribute.attr 等悬空 → 后续解析返回错误类型。
# 在 ParseVpiIntoModule 末尾统一释放。
if tree is None:
stdlib.free(vpi_buf)
return 0
# 提取源模块 SHA1(用于函数名混淆,在 AST 解析成功后提取避免提前返回时泄漏)
src_sha1: str = _extract_sha1_from_path(vpi_path)
# 从 vpi_buf 文本直接提取方法返回注解字符串缓存到 stdlib
# (不依赖 AST tree:tree 深层节点在后续遍历处理时可能被覆盖损坏,
# 但 vpi_buf 原始文本在 ParseVpiIntoModule 末尾才释放,始终有效)
_cache_vpi_return_annots_text(vpi_buf, src_sha1)
# 同源:提取函数/方法参数注解字符串缓存(tree 参数列表同样可能被
# 覆盖截断,_inject_*_declare 用此缓存补全 declare 参数数)
_cache_vpi_param_annots_text(vpi_buf, src_sha1)
# 3. 遍历 AST 顶层节点
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
if ch is None:
if src_sha1 is not None:
stdlib.free(src_sha1)
return 1
n: t.CSizeT = len(ch)
# 收集 stub 内被读模块自己的 import 语句,构建独立的注解解析上下文。
# .vpi stub 顶部含被读模块的 import(Converter 已写入,如 string.vp 的
# "import t, c"),用它替代当前模块的 imported_modules/from_imports 去
# 解析注解——根治跨上下文误报(如 t.CArray[str] 需要 't' 已导入)。
stub_imported_modules: str = None
stub_from_imports: str = None
si: t.CSizeT
for si in range(n):
schild: ast.AST | t.CPtr = ch.get(si)
if schild is None:
continue
sk: int = schild.kind()
if sk == ast.ASTKind.Import:
simp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(schild)
if simp.names is not None:
sn_n: t.CSizeT = len(simp.names)
sni: t.CSizeT
for sni in range(sn_n):
sal: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(simp.names.get(sni))
if sal is not None and sal.name is not None:
stub_imported_modules = _stub_add_imported(
pool, stub_imported_modules, sal.name)
if sal.asname is not None:
stub_imported_modules = _stub_add_imported(
pool, stub_imported_modules, sal.asname)
elif sk == ast.ASTKind.ImportFrom:
sifr: ast.ImportFrom | t.CPtr = (ast.ImportFrom | t.CPtr)(schild)
sifr_mod: str = sifr.module
if sifr_mod is not None:
stub_imported_modules = _stub_add_imported(
pool, stub_imported_modules, sifr_mod)
if sifr.names is not None:
sifr_nn: t.CSizeT = len(sifr.names)
sifri: t.CSizeT
for sifri in range(sifr_nn):
sial: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(sifr.names.get(sifri))
if sial is not None and sial.name is not None:
# '*' star import 不建 from 映射(lookup 有 star 回退)
if string.strcmp(sial.name, "*") != 0:
slocal: str = sial.name
sorig: str = None
if sial.asname is not None:
slocal = sial.asname
sorig = sial.name
stub_from_imports = _stub_add_from(
pool, stub_from_imports, slocal, sifr_mod, sorig)
# 用 stub 自己的 imports 替代当前模块的(stub 注解解析上下文正确性)
imported_modules: str = trans._imported_modules
from_imports: str = trans._from_imports
if stub_imported_modules is not None:
imported_modules = stub_imported_modules
if stub_from_imports is not None:
from_imports = stub_from_imports
# 把源模块自己加入 stub 注解上下文:stub 注解(如 __sha512 的 transform
# 参数 t.CArray[t.CUInt8T, SHA512_BLOCK_LEN])引用源模块自身的 CDefine,
# 但 stub 的 import 只含被 import 的模块(t/c/string),不含自己。
# 不加则 _eval_const_int_expr 跨模块 CDefine 查找遍历不到源模块
# (imported_modules 无 __sha512)→ t.CArray count 报"必须是整数常量"。
# 示例:RelPath "hashlib/__sha512.vp" → 模块名 "hashlib.__sha512"。
if src_sha1 is not None:
stub_rel_src: str = StubMergerStore.LookupSha1RelPath(src_sha1)
if stub_rel_src is not None:
sr_len_src: t.CSizeT = string.strlen(stub_rel_src)
if sr_len_src > 3 and string.strncmp(stub_rel_src + sr_len_src - 3, ".vp", 3) == 0:
sr_len_src -= 3
if sr_len_src > 0:
src_mod_buf: str = stdlib.malloc(sr_len_src + 1)
if src_mod_buf is not None:
sri_src: t.CSizeT
for sri_src in range(sr_len_src):
ch_src: t.CChar = stub_rel_src[sri_src]
if ch_src == '/' or ch_src == '\\':
src_mod_buf[sri_src] = '.'
else:
src_mod_buf[sri_src] = ch_src
src_mod_buf[sr_len_src] = '\0'
imported_modules = _stub_add_imported(pool, imported_modules, src_mod_buf)
stdlib.free(src_mod_buf)
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 函数表信息
funcs_ptr: GenTypes.FuncEntry | t.CPtr = trans._funcs
max_funcs: int = 1024
# ============================================================
# 第一遍:先注册所有 ClassDef(含字段 + 方法 declare),再处理
# FunctionDef/AnnAssign。此前单遍顺序处理,类定义位于全局变量之后时
# (如 devfs.vp:`directories: t.CArray[devfs_directory, MAX_DIRECTORIES]`
# 在 `class devfs_directory` 之前),AnnAssign 全局变量解析裸类名时类
# 未注册 → CArray 元素解析失败 fatal(VKernel PhaseA 根因)。
# ============================================================
i0: t.CSizeT
for i0 in range(n):
child0: ast.AST | t.CPtr = ch.get(i0)
if child0 is None:
continue
if child0.kind() != ast.ASTKind.ClassDef:
continue
class_cd0: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child0)
if class_cd0 is None or class_cd0.name is None:
continue
cls_name_v0: str = class_cd0.name
# 泛型类(class X[T])跳过:stub 模板签名含类级类型参数 T,注入时
# T 无法确定;特化方法 define 已在 Phase1 生成,无需 stub 模板 declare。
tp_stub0: list[str] | t.CPtr = class_cd0.type_params
if tp_stub0 is not None and len(tp_stub0) > 0:
continue
_inject_class_struct_declare(pool, mod, class_cd0, cls_name_v0, trans,
src_sha1, imported_modules, from_imports)
cls_children0: list[ast.AST | t.CPtr] | t.CPtr = class_cd0.children
if cls_children0 is not None:
cn20: t.CSizeT = len(cls_children0)
for ci20 in range(cn20):
m_child0: ast.AST | t.CPtr = cls_children0.get(ci20)
if m_child0 is None:
continue
if m_child0.kind() != ast.ASTKind.FunctionDef:
continue
m_fd0: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m_child0)
if m_fd0 is None or m_fd0.name is None:
continue
_inject_method_declare(pool, mod, m_fd0, cls_name_v0, trans,
src_sha1, imported_modules, from_imports,
i32_ty, i8_ptr_ty, funcs_ptr, max_funcs)
i: t.CSizeT
for i in range(n):
child: ast.AST | t.CPtr = ch.get(i)
if child is None:
continue
kd: int = child.kind()
# FunctionDef → 提取函数名、参数、返回类型 → 注入 declare
if kd == ast.ASTKind.FunctionDef:
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
if fd is None or fd.name is None:
continue
_inject_function_declare(pool, mod, fd, trans, src_sha1,
imported_modules, from_imports,
i32_ty, i8_ptr_ty,
funcs_ptr, max_funcs)
continue
# AnnAssign with t.CTypedef → 注册 CTypedef;
# 普通模块级全局变量(如 tokens._op_head)→ external global declare
if kd == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(child)
if aa is None:
continue
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
dbg_tn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.target)
if dbg_tn.id is not None and dbg_tn.id[0] != '\0': # 存疑
if string.strcmp(dbg_tn.id, "irq_handler_t") == 0 \
or string.strcmp(dbg_tn.id, "gdt") == 0 \
or string.strcmp(dbg_tn.id, "KBD_BUF_SIZE") == 0:
dbg_ct: int = GenApi._is_ctypedef_annotation(aa.annotation)
dbg_cd: int = GenApi.is_cdefine_annotation(aa.annotation)
if GenApi._is_ctypedef_annotation(aa.annotation) != 0:
_register_ctypedef_from_annassign(pool, aa)
elif GenApi.is_cdefine_annotation(aa.annotation) != 0:
# vpi 里 CDefine 定义(如 keyboard.vp 的 KBD_BUF_SIZE: t.CDefine = 1024)
# 注册到当前文件 CDefine 表,供同 vpi 后续 t.CArray count 引用解析
# (否则跨模块 count 查找 [CDEF-MISS] fail-fast,VKernel PhaseA 根因)
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name \
and aa.value is not None:
cdef_tgt: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.target)
if cdef_tgt.id is not None:
# 字符串 CDefine(版本号等)跳过:extract_cdefine_int_value
# 对字符串常量 fail-fast,且 count 引用只关心整数 CDefine
cdef_is_str: int = 0
cdef_is_float: int = 0
cdef_float_val: double = 0.0
if aa.value.kind() == ast.ASTKind.Constant:
cn_v: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value)
if cn_v.const_kind == ast.CONST_STR:
cdef_is_str = 1
elif cn_v.const_kind == ast.CONST_FLOAT:
cdef_is_float = 1
cdef_float_val = cn_v.float_val
if cdef_is_str == 0 and cdef_is_float != 0:
# 浮点 CDefine(如 FRAGMENTATION_THRESHOLD: t.CDefine = 0.7)
# 用 register_cdefine_double 注册(走整数提取会 fail-fast)
GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val)
elif cdef_is_str == 0:
cdef_val: t.CInt64T = GenApi.extract_cdefine_int_value(aa.value)
GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val)
else:
_inject_global_variable_declare(pool, mod, aa, trans,
stub_imported_modules, stub_from_imports,
i32_ty, i8_ptr_ty, src_sha1)
continue
# ClassDef → 已在第一遍统一注册(含字段 + 方法 declare),此处跳过
# (原单遍顺序处理在类定义位于全局变量之后时,AnnAssign 全局变量
# 解析裸类名找不到类 → CArray 元素解析失败 fatal,见上方第一遍注释)
# Import/ImportFrom → 跳过
# (import 不需要处理)
# 释放 SHA1 缓冲区(_extract_sha1_from_path 用 stdlib.malloc 分配)
if src_sha1 is not None:
stdlib.free(src_sha1)
# vpi_buf 延迟到全部 tree 处理完成后释放(见解析处注释)
stdlib.free(vpi_buf)
return 1
# ============================================================
# _inject_class_struct_declare - 从 vpi ClassDef 注册类为 struct 类型
#
# 背景:vpi stub 注入(ParseVpiIntoModule)此前只注入类内方法 declare,
# 不注册类本身 → 严格模式下(strict_mode:true)同一 vpi 内后续 AnnAssign
# 全局变量的裸类名注解(如 gdt.vp 的 `gdt: t.CArray[gdt_entry, 7]` 元素
# gdt_entry)is_struct_visible=0 → resolve_annotation_type 返回 None →
# TypeGenResolve CArray 元素解析失败 fatal(ViperOS VKernel apic.vp 编译
# 根因)。源模块编译时 ClassGenDef.translate_class_def 会 add_visible_struct
# + register_struct(含字段),vpi 注入缺同一注册 → 此处补齐:
# 1. 已注册(find_struct_by_module 按 name+sha1 命中,含源模块编译时注册)
# → 仅 add_visible_struct 标记当前文件可见
# 2. 未注册 → 解析字段类型 → 创建命名 StructType("sha1.ClassName") →
# register_struct + add_visible_struct
# 字段解析失败不 fatal(不注册该类,留给源模块编译注册;若 vpi 内后续
# 裸类名引用它则由调用侧 fail-fast 暴露)。
# ============================================================
def _inject_class_struct_declare(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
class_cd: ast.ClassDef | t.CPtr,
cls_name: str,
trans: HT.Translator | t.CPtr,
src_sha1: str,
imported_modules: str,
from_imports: str) -> int:
"""从 vpi ClassDef 注册类为 struct 类型(含字段),使同 vpi 内裸类名注解可解析"""
if pool is None or mod is None or class_cd is None or cls_name is None:
return 0
# 1. 已注册检查(含源模块编译时注册的 entry):仅标记可见,避免重复注册
if src_sha1 is not None and src_sha1[0] != '\0':
existing_c: GenTypes.StructEntry | t.CPtr = GenApi.find_struct_by_module(cls_name, src_sha1)
if existing_c is not None and existing_c.Ty is not None:
GenApi.add_visible_struct(pool, cls_name)
return 1
# 2. 收集字段类型(AnnAssign 子节点,仿 ClassGenDef.translate_class_def)
field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
if field_names_buf is None or field_types_buf is None:
return 0
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
field_count: int = 0
cls_children_c: list[ast.AST | t.CPtr] | t.CPtr = class_cd.children
if cls_children_c is not None:
cn_c: t.CSizeT = len(cls_children_c)
for ci_c in range(cn_c):
m_child_c: ast.AST | t.CPtr = cls_children_c.get(ci_c)
if m_child_c is None:
continue
if m_child_c.kind() != ast.ASTKind.AnnAssign:
continue
m_aa_c: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(m_child_c)
if m_aa_c is None or m_aa_c.target is None:
continue
if m_aa_c.target.kind() != ast.ASTKind.Name:
continue
m_tgt_c: ast.Name | t.CPtr = (ast.Name | t.CPtr)(m_aa_c.target)
if m_tgt_c.id is None:
continue
field_ty_c: llvmlite.LLVMType | t.CPtr = GenApi.resolve_annotation_type(
pool, m_aa_c.annotation, imported_modules, from_imports, trans, 0)
if field_ty_c is None:
# 字段类型解析失败:不注册该类(留给源模块编译注册),不 fatal
return 0
if field_count < GenTypes.FIELD_MAX:
field_names_buf[field_count] = t.CSizeT(m_tgt_c.id)
field_types_buf[field_count] = t.CSizeT(field_ty_c)
field_count += 1
# 3. 构建 ParamNode 链表
first_node_c: llvmlite.ParamNode | t.CPtr = None
prev_node_c: llvmlite.ParamNode | t.CPtr = None
for fi_c in range(field_count):
fty_c: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(
t.CVoid(field_types_buf[fi_c], t.CPtr))
pnode_c: llvmlite.ParamNode | t.CPtr = pool.alloc(llvmlite.ParamNode.__sizeof__())
if pnode_c is None:
continue
string.memset(pnode_c, 0, llvmlite.ParamNode.__sizeof__())
pnode_c.Ty = fty_c
if first_node_c is None:
first_node_c = pnode_c
if prev_node_c is not None:
prev_node_c.Next = pnode_c
prev_node_c = pnode_c
# 4. 命名结构体类型名 "sha1.ClassName"(与定义侧 ClassGenDef 一致)
type_name_c: str = cls_name
if src_sha1 is not None and src_sha1[0] != '\0':
name_buf_c: str = pool.alloc(64)
if name_buf_c is not None:
viperlib.snprintf(name_buf_c, 64, "%s.%s", src_sha1, cls_name)
type_name_c = name_buf_c
struct_ty_c: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(
pool, first_node_c, field_count, type_name_c)
if struct_ty_c is None:
return 0
# 5. StdlibAlloc(Phase B)时深拷贝到 perm_pool,跨文件 reset 存活
# (对齐 ClassGenDef.translate_class_def L489-494)
if GenApi.get_stdlib_alloc() != 0:
st_copy_c: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(
CompilerFactory.get_perm_pool(), struct_ty_c)
if st_copy_c is not None:
struct_ty_c = st_copy_c
# 6. 注册到当前模块(输出类型定义行)+ 全局 StructTable + 可见性
llvmlite.module_add_named_type(mod, pool, struct_ty_c)
entry_c: GenTypes.StructEntry | t.CPtr = GenApi.register_struct(
pool, cls_name, struct_ty_c, src_sha1)
if entry_c is None:
return 0
GenApi.add_visible_struct(pool, cls_name)
return 1
# ============================================================
# _inject_global_variable_declare - 从 vpi AnnAssign 创建模块级全局变量 declare
#
# 背景:vpi 里模块级全局变量(如 tokens.vp 的 _op_head: OpEntry | t.CPtr = None)
# 需在当前模块创建 external global 声明(短名 @name,COFF 按名链接),否则
# 跨模块 from-import 读取全局变量时 find_global_in_module 找不到 →
# "跨模块全局变量类型未知(stub 未注入)" 误杀(lexer.vp 读 tokens._op_head)。
# ============================================================
def _inject_global_variable_declare(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
aa: ast.AnnAssign | t.CPtr,
trans: HT.Translator | t.CPtr,
imported_modules: str,
from_imports: str,
i32_ty: llvmlite.LLVMType | t.CPtr,
i8_ptr_ty: llvmlite.LLVMType | t.CPtr,
src_sha1: str) -> None:
"""从 vpi AnnAssign 创建模块级全局变量 external declare(mangled 名 @<src_sha1>.<name>)"""
if pool is None or mod is None or aa is None or trans is None:
return
tgt_v: ast.AST | t.CPtr = aa.target
if tgt_v is None or tgt_v.kind() != ast.ASTKind.Name:
return
nm_v: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tgt_v)
if nm_v is None or nm_v.id is None:
return
gname_v: str = nm_v.id
if gname_v[0] == '\0':
return
# mangled 名:带源模块 SHA1 前缀(对齐定义端 5f7a873 的 @<sha1>.<name>
# 与查找端 from-import 精确查找三方一致;裸短名会退化为未定义)。
# 注:a42923c 曾把此处写成未定义变量 gv_name_m(笔误,mangle 未实现),
# 导致注入空名 @ = external global → llc "expected top-level entity"。
gv_name_m: str = gname_v
# CExport 全局:定义端用裸名(@gp),stub 声明必须同步裸名,否则跨模块
# 引用生成 @sha1.gp → 与定义端 @gp 不匹配 → ld.lld undefined symbol
# (gdt.vp gdt/gp、idt.vp idt、sched.vp _sched_ptr/_switch_*_rsp、
# coroutine.vp _coro_*、process.vp _saved_cr3 等 CExport 全局根因)。
is_gv_cexport: int = 0
if aa.annotation is not None:
is_gv_cexport = GenApi.has_decorator_marker(aa.annotation, "CExport")
if src_sha1 is not None and src_sha1[0] != '\0' and is_gv_cexport == 0:
ml_v: t.CSizeT = string.strlen(src_sha1) + 1 + string.strlen(gname_v) + 1
gv_name_m = pool.alloc(ml_v)
if gv_name_m is not None:
viperlib.snprintf(gv_name_m, ml_v, "%s.%s", src_sha1, gname_v)
# 去重:遍历模块全局变量链表(mangled 名匹配)
cur_v: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
while cur_v is not None:
if cur_v.Name is not None and string.strcmp(cur_v.Name, gv_name_m) == 0:
return
cur_v = cur_v.Next
# 解析注解类型(裸类 → Ptr(struct),CArray → 元素指针)
resolved_v: llvmlite.LLVMType | t.CPtr = None
if aa.annotation is not None:
resolved_v = GenApi.resolve_annotation_type(
pool, aa.annotation, imported_modules, from_imports, trans)
if resolved_v is None:
return # 类型解析失败跳过:调用侧有 fail-fast 定位
gv_ty_v: llvmlite.LLVMType | t.CPtr = resolved_v
match resolved_v:
case llvmlite.LLVMType.Struct(_, _, _):
# 对象类(@t.Object/@t.CVTable:对象即指针,HasNew/HasVTable/IsOOP)
# → Ptr(struct),与定义端 handle_module_level_var 一致;否则纯
# t.CStruct 值类(如 pci_device)保持值实例。此前一律转 Ptr 导致
# 跨模块字段访问把实例前 8 字节当指针解引用 → 垃圾地址 #GP
# (uhci.vp pci._uhci_dev.irq 根因);一律保持值则 @t.Object 类
# 模块级变量(如 devfs.vp serial0_file.open)按指针访问读错 vtable
# → 调用跳栈 #PF。
gv_entry_v: GenTypes.StructEntry | t.CPtr = GenApi.find_struct_by_type(resolved_v)
if gv_entry_v is not None \
and (gv_entry_v.HasNew != 0 or gv_entry_v.HasVTable != 0 or gv_entry_v.IsOOP != 0):
gv_ty_v = llvmlite.Ptr(pool, resolved_v)
else:
gv_ty_v = resolved_v
case llvmlite.LLVMType.Array(aelem_v, acount_v):
# 定长数组(CArray[T, N]):保留完整数组类型 [N x T],与定义端
# handle_module_level_var 生成的 global [N x T] 一致——此前一律
# 衰减为元素指针 i32*,链接后引用端 load 把数组前 8 字节当指针
# 读 → 垃圾地址 → zdeflate_len_to_symbol 读 NULL/垃圾崩溃
# (zdeflate_len_base 跨模块数组根因)。
if acount_v > 0:
gv_ty_v = resolved_v
else:
# 不定长数组(CArray[T, None]):元素指针(C 数组衰减)
gv_ty_v = llvmlite.Ptr(pool, aelem_v)
case _:
gv_ty_v = resolved_v
# 确保跨模块结构体类型在模块中有 opaque 声明:external global 类型
# (如 [7 x %gdt_entry])内嵌 struct 无任何定义时,llc 报 "use of
# undefined type named 'sha1.gdt_entry'"(fpu/fat32 合并 IR 根因)。
# 对齐 TranslatorGenMixin 创建全局前的 module_ensure_opaque_for_type
# (_scan_type_for_cross_module_refs 递归 Array/Struct/Ptr)。
llvmlite.module_ensure_opaque_for_type(mod, pool, gv_ty_v)
gv_v: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name_m, gv_ty_v)
if gv_v is None:
return
llvmlite.module_add_global(mod, gv_v)
gv_v.Linkage = "external"
# ============================================================
# _inject_function_declare - 为单个 FunctionDef 创建 declare 声明
#
# 检测 CExport/CExtern/State 标记,计算 mangled name,
# 用 find_func_in_module 去重,创建 declare 并添加参数,
# 注册到函数表。
# ============================================================
def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
fd: ast.FunctionDef | t.CPtr,
trans: HT.Translator | t.CPtr,
src_sha1: str,
imported_modules: str,
from_imports: str,
i32_ty: llvmlite.LLVMType | t.CPtr,
i8_ptr_ty: llvmlite.LLVMType | t.CPtr,
funcs_ptr: GenTypes.FuncEntry | t.CPtr,
max_funcs: int) -> None:
"""为 FunctionDef 创建 declare 声明并注入到模块"""
if fd.name is None:
return
# 检测 CExtern/State/CExport 标记(从返回类型注解)
has_extern: int = 0
has_state: int = 0
has_export: int = 0
if fd.returns is not None:
has_extern = GenApi.has_decorator_marker(fd.returns, "CExtern")
has_state = GenApi.has_decorator_marker(fd.returns, "State")
has_export = GenApi.has_decorator_marker(fd.returns, "CExport")
# 计算 mangled name
# CExport/CExtern/State/main 函数用裸名,其他函数用源模块 SHA1 前缀
use_bare_name: int = 0
if has_extern != 0 or has_state != 0 or has_export != 0 or string.strcmp(fd.name, "main") == 0:
mangled_name: str = fd.name
use_bare_name = 1
else:
mangled_name = GenApi._mangle_name_with_sha1(src_sha1, fd.name)
# 去重:如果函数已存在则跳过
existing: llvmlite.Function | t.CPtr = GenApi.find_func_in_module(mod, mangled_name)
if existing is not None:
return
# 也检查裸名(非裸名模式时,CExport 函数可能已通过其他途径注册)
if use_bare_name == 0:
existing_bare: llvmlite.Function | t.CPtr = GenApi.find_func_in_module(mod, fd.name)
if existing_bare is not None:
return
# 推断返回类型
# 优先用缓存的返回注解字符串(tree 解析后立即提取,节点未损坏)
# 直接字符串解析(绕开 _parse_type_string 跨模块 AST 构造在 TPV 中的
# __init__/vtable 未执行问题,节点 kind() 异常导致 resolve 得垃圾类型)
ret_ty: llvmlite.LLVMType | t.CPtr = None
ret_annot_fd: str = _lookup_vpi_ret_annot(src_sha1, None, fd.name)
if ret_annot_fd is not None:
ret_ty = _resolve_vpi_ret_annot_str(pool, ret_annot_fd, i32_ty, i8_ptr_ty)
if ret_ty is None and fd.returns is not None:
ret_ty = GenApi.resolve_annotation_type(
pool, fd.returns, imported_modules, from_imports, trans)
# 如果返回类型是纯 t.State(无实际类型),用 Void
if ret_ty is None and fd.returns is not None:
if has_state != 0:
ret_ty = llvmlite.Void(pool)
# 默认返回 i32
if ret_ty is None:
ret_ty = i32_ty
# 创建 declare
func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, mangled_name, ret_ty)
if func is None:
return
# 提取参数并添加到 declare
param_count: int = 0
fd_defaults: t.CVoid | t.CPtr = None
fd_default_count: int = 0
if fd.args is not None:
args_node: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args)
if args_node.args is not None:
args_list: list[ast.AST | t.CPtr] | t.CPtr = args_node.args
param_count = len(args_list)
# 参数数上限哨兵(fail-fast):vpi tree 深层节点在遍历处理过程中
# 可能被覆盖损坏(见本文件顶部注释),fd.args 悬空时 __len__()
# 返回垃圾大数 → for 循环 add_param 海量次 → Params 链超长,
# dump 时步数哨兵误报"链表成环"。超限立即报错定位。
if param_count < 0 or param_count > 128:
stdio.printf(
"[VPIS] 参数数异常: 函数名=%s param_count=%d(fd.args=%llu,tree 可能被覆盖,终止)\n",
fd.name, param_count, t.CUInt64T(fd.args))
stdlib.exit(1)
pi: t.CSizeT
for pi in range(param_count):
ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(args_list.get(pi))
if ag is None:
continue
pname: str = ag.arg
if pname is None:
pname = "arg"
# 解析参数类型
param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty
# 优先用 vpi 文本注解缓存解析参数类型(与返回类型同源——vpi
# tree 深层节点 kind() 可能损坏,AST resolve 得垃圾类型如
# t.CSizeT→i32,跨模块 declare 参数宽错 → 调用侧 trunc 传参、
# callee 按宽类型读栈残留垃圾高位 → 参数巨值崩溃(SlotsBugTest
# 根因)。段索引 = pi(模块级函数无 self,0=首参)。
pseg_ty: llvmlite.LLVMType | t.CPtr = _resolve_vpi_param_annot_seg(
pool, src_sha1, None, fd.name, pi, i32_ty, i8_ptr_ty)
if pseg_ty is not None:
match pseg_ty:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Int64(pool)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
param_ty = pseg_ty
elif ag.annotation is not None:
resolved_ty: llvmlite.LLVMType | t.CPtr = GenApi.resolve_annotation_type(
pool, ag.annotation, imported_modules, from_imports, trans)
if resolved_ty is not None:
# 结构体按值参数 → i64:跨模块结构体在本模块是 opaque
# (未定义字段),LLVM 不允许 opaque struct 作为函数参数
# (llc: invalid type for function argument)。Windows ABI
# 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。
# CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。
match resolved_ty:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Int64(pool)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
param_ty = resolved_ty
llvmlite.add_param(pool, func, param_ty, pname)
# 文本缓存校验/修复:vpi tree args 可能被覆盖损坏(尾部默认参数丢失)
# → declare 参数数不足 → 调用点缺参 → 被调函数读栈垃圾。用 vpi 文本
# 缓存的参数注解补全 declare 参数(与 define 参数数一致)。
param_count = _repair_declare_params_from_text(
pool, func, src_sha1, None, fd.name, param_count, i32_ty, i8_ptr_ty)
# *args 变长参数:标记 IsVarArg=1,使调用侧走 C ABI 参数提升
# (i8/i16 → i32,float → double),避免 %d/%x 等格式因参数按 i64
# 传递而读错(第二个 %x 读到第一个 i64 槽的高 4 字节)。
if args_node.vararg is not None:
func.IsVarArg = 1
# 默认参数:vpi 签名含 '= <default>'(Converter 已输出),提取 defaults
# 列表注册默认参数表——跨模块缺参调用(如 resolve_annotation_type 的
# depth=0)才能按表填充,否则被调函数读到栈垃圾(假报递归过深/崩溃)。
if args_node.defaults is not None:
fd_dlist: list[ast.AST | t.CPtr] | t.CPtr = args_node.defaults
fd_default_count = len(fd_dlist)
if fd_default_count < 0 or fd_default_count > 128:
# vpi tree 深层节点(args.defaults)可能被覆盖损坏,len 返回
# 垃圾大数 → _register_global_defaults 按垃圾大数 alloc + 数组
# 越界写 perm_pool(覆盖当前模块 AST/表,SlotsBugTest 崩溃根因
# 候选)。与 param_count 哨兵(L1493-1497)对齐:垃圾值放弃
# 注册默认值(跨模块缺参调用走零值兜底),阻断越界写。
stdio.printf(
"[VPIS] 默认参数数异常: 函数名=%s default_count=%d(tree 可能被覆盖,跳过默认值注册)\n",
fd.name, fd_default_count)
fd_defaults = None
fd_default_count = 0
else:
fd_defaults = fd_dlist
# 注册到函数表(用裸名 fd.name,不是 mangled_name)
if funcs_ptr is not None:
cur_count: int = trans._func_count
if GenApi.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs,
fd_defaults, fd_default_count, param_count) == 0:
trans._func_count = cur_count + 1
# 补全全局函数表 TypeStr(参数已全部添加,对齐 FuncGenDef L211 行为):
# vpi 注入路径若不补全,跨模块 vtable/调用解析 find_func_entry_global()
# 时 TypeStr 为空 → "vtable 方法找不到函数实现"(依赖模块晚于本模块编译时必现)
ExprCallTable.update_global_func_typestr(pool, fd.name, func)
# ============================================================
# _inject_method_declare - 为 ClassDef 内的方法创建 declare
#
# 方法混淆名: {src_sha1}.{ClassName}.{method}
# 参数类型从 vpi 注解解析(self 无注解时默认为 i8*)。
# 注入正确签名避免调用时 _ensure_method_declare 用实参类型
# 创建错误 declare(如 size: t.CSizeT 被写成 i32 而非 i64)。
# ============================================================
def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
m_fd: ast.FunctionDef | t.CPtr,
cls_name: str,
trans: HT.Translator | t.CPtr,
src_sha1: str,
imported_modules: str,
from_imports: str,
i32_ty: llvmlite.LLVMType | t.CPtr,
i8_ptr_ty: llvmlite.LLVMType | t.CPtr,
funcs_ptr: GenTypes.FuncEntry | t.CPtr,
max_funcs: int) -> None:
"""为 ClassDef 内方法创建 declare 声明并注入到模块"""
if m_fd is None or m_fd.name is None or cls_name is None:
return
if src_sha1 is None:
return
# 构造方法查找名: ClassName.method
lookup_name_len: t.CSizeT = string.strlen(cls_name) + 1 + string.strlen(m_fd.name) + 1
lookup_name: str = pool.alloc(lookup_name_len)
if lookup_name is None:
return
viperlib.snprintf(lookup_name, lookup_name_len, "%s.%s", cls_name, m_fd.name)
# mangled name: {src_sha1}.{ClassName}.{method}
mangled_len: t.CSizeT = string.strlen(src_sha1) + 1 + lookup_name_len
mangled_name: str = pool.alloc(mangled_len)
if mangled_name is None:
return
viperlib.snprintf(mangled_name, mangled_len, "%s.%s", src_sha1, lookup_name)
# 去重:如果方法已存在则跳过
existing: llvmlite.Function | t.CPtr = GenApi.find_func_in_module(mod, mangled_name)
if existing is not None:
return
# 推断返回类型
# 优先用缓存的返回注解字符串(tree 解析后立即提取,节点未损坏),
# 用 _parse_type_string 重新解析成新 AST(立即使用不被覆盖)。
# 直接解析 m_fd.returns 可能因 vpi tree 深层节点被覆盖而得到垃圾类型。
ret_ty: llvmlite.LLVMType | t.CPtr = None
ret_annot: str = _lookup_vpi_ret_annot(src_sha1, cls_name, m_fd.name)
if ret_annot is not None:
ret_ty = _resolve_vpi_ret_annot_str(pool, ret_annot, i32_ty, i8_ptr_ty)
if ret_ty is None and m_fd.returns is not None:
ret_ty = GenApi.resolve_annotation_type(
pool, m_fd.returns, imported_modules, from_imports, trans)
# __new__ 无返回注解时推断为类指针(__new__ 返回 self 指针,默认 i32 会截断指针)
if ret_ty is None and string.strcmp(m_fd.name, "__new__") == 0:
cls_ty: llvmlite.LLVMType | t.CPtr = GenApi.get_struct_type(cls_name)
if cls_ty is not None:
ret_ty = llvmlite.Ptr(pool, cls_ty)
if ret_ty is None:
ret_ty = i32_ty
# 创建 declare
func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, mangled_name, ret_ty)
if func is None:
return
# 提取参数并添加到 declare
param_count: int = 0
md_defaults: t.CVoid | t.CPtr = None
md_default_count: int = 0
if m_fd.args is not None:
args_node: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(m_fd.args)
if args_node.args is not None:
args_list: list[ast.AST | t.CPtr] | t.CPtr = args_node.args
param_count = len(args_list)
# 参数数上限哨兵(fail-fast):与 _inject_function_declare 同因
# (vpi tree 深层节点被覆盖损坏时 __len__() 返回垃圾大数)。
if param_count < 0 or param_count > 128:
stdio.printf(
"[VPIS] 参数数异常: 方法名=%s.%s param_count=%d(m_fd.args=%llu,tree 可能被覆盖,终止)\n",
cls_name, m_fd.name, param_count, t.CUInt64T(m_fd.args))
stdlib.exit(1)
pi: t.CSizeT
for pi in range(param_count):
ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(args_list.get(pi))
if ag is None:
continue
pname: str = ag.arg
if pname is None:
pname = "arg"
# self/cls 参数(首参):跨模块声明用类指针(%"sha1.Class"*)而非
# 默认 i8* —— 与定义侧 _translate_method 的 self 参数 Ptr(StructTy)
# 签名一致,消除跨模块声明/定义歧义。调用侧按形参 coerce 自动
# bitcast(见 _call_method_on_ptr/_translate_method_call 的 coerce
# 块)。类类型未注册时兜底走下方注解解析/i8*(保持原行为)。
if pi == 0 and (string.strcmp(pname, "self") == 0
or string.strcmp(pname, "cls") == 0):
cls_ty_sd: llvmlite.LLVMType | t.CPtr = GenApi.get_struct_type(cls_name)
if cls_ty_sd is not None:
llvmlite.add_param(pool, func, llvmlite.Ptr(pool, cls_ty_sd), pname)
continue
param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty
# 优先用 vpi 文本注解缓存解析参数类型(与返回类型同源——vpi
# tree 深层节点 kind() 可能损坏,AST resolve 得垃圾类型如
# t.CSizeT→i32,declare 参数宽错 → 调用侧 trunc i64→i32 传参、
# callee 按 i64 读栈残留垃圾高位 → can=巨值读未写槽位崩溃,
# SlotsBugTest 根因)。段索引 = pi(0=self 空段)。
pseg_ty: llvmlite.LLVMType | t.CPtr = _resolve_vpi_param_annot_seg(
pool, src_sha1, cls_name, m_fd.name, pi, i32_ty, i8_ptr_ty)
if pseg_ty is not None:
match pseg_ty:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Int64(pool)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
param_ty = pseg_ty
elif ag.annotation is not None:
resolved_ty: llvmlite.LLVMType | t.CPtr = GenApi.resolve_annotation_type(
pool, ag.annotation, imported_modules, from_imports, trans)
if resolved_ty is not None:
# 结构体按值参数 → i64:跨模块结构体在本模块是 opaque
# (未定义字段),LLVM 不允许 opaque struct 作为函数参数
# (llc: invalid type for function argument)。Windows ABI
# 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。
# CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。
match resolved_ty:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Int64(pool)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
param_ty = resolved_ty
llvmlite.add_param(pool, func, param_ty, pname)
# 文本缓存校验/修复:vpi tree args 可能被覆盖损坏(尾部默认参数丢失)
# → declare 参数数不足 → 调用点缺参 → 被调函数读栈垃圾(跨模块 CDefine
# 查找 depth=160 假递归根因)。用 vpi 文本缓存的参数注解补全 declare
# 参数(与 define 参数数一致,调用点默认参数填充才能生效)。
param_count = _repair_declare_params_from_text(
pool, func, src_sha1, cls_name, m_fd.name, param_count, i32_ty, i8_ptr_ty)
# OOP iter 协议:__next__ 方法 stub 声明带隐藏 __stop_iter_flag__(i1*),
# 与 _translate_method 的 define 签名一致(对齐 TPC HandlesFunctions.py
# L703-705)。for x in obj 循环会以 2 个实参调用 __next__(self, flag)。
if m_fd.name is not None and string.strcmp(m_fd.name, "__next__") == 0:
i1_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
if i1_ty_si is not None:
i1_ptr_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i1_ty_si)
if i1_ptr_ty_si is not None:
llvmlite.add_param(pool, func, i1_ptr_ty_si, "__stop_iter_flag__")
# 默认参数:vpi 签名含 '= <default>'(Converter 已输出),提取 defaults
# 列表注册默认参数表——跨模块缺参调用(如 resolve_annotation_type 的
# depth=0)才能按表填充,否则被调函数读到栈垃圾(假报递归过深/崩溃)。
if m_fd.args is not None:
md_args_node: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(m_fd.args)
if md_args_node.defaults is not None:
md_dlist: list[ast.AST | t.CPtr] | t.CPtr = md_args_node.defaults
md_default_count = len(md_dlist)
if md_default_count < 0 or md_default_count > 128:
# 同 _inject_function_declare:vpi tree 深层节点被覆盖时
# defaults len 可能为垃圾大数 → 越界写 perm_pool。放弃注册。
stdio.printf(
"[VPIS] 默认参数数异常: 方法名=%s.%s default_count=%d(tree 可能被覆盖,跳过默认值注册)\n",
cls_name, m_fd.name, md_default_count)
md_defaults = None
md_default_count = 0
else:
md_defaults = md_dlist
# 注册到函数表(用裸名 ClassName.method,与调用方 lookup_name 一致)
# ParamCount 不含 self(方法首参 self/cls 不计入,与 _translate_method 的
# md_param_count = len(args) - 1 保持一致)——默认参数填充按非 self
# 参数计数,若含 self 会导致 mdf_param_count 虚高、填充逻辑错位/越界。
if funcs_ptr is not None:
cur_count: int = trans._func_count
param_count_no_self: int = param_count
if param_count_no_self > 0:
param_count_no_self -= 1
if GenApi.add_func_to_table(funcs_ptr, cur_count, lookup_name, func, max_funcs,
md_defaults, md_default_count, param_count_no_self) == 0:
trans._func_count = cur_count + 1
# 补全全局函数表 TypeStr(参数已全部添加,对齐 ClassGenVTable L832 行为):
# vpi 注入的方法声明若不补全 TypeStr,跨模块继承 vtable 生成时
# find_func_entry_global().TypeStr 为空 → "vtable 方法找不到函数实现"
# (astaux.vp 先于 ast.base.vp 编译时必现:AST.accept 仅有 vpi 注入条目)
ExprCallTable.update_global_func_typestr(pool, lookup_name, func)
# ============================================================
# _register_ctypedef_from_annassign - 从 AnnAssign 节点注册 CTypedef
#
# 提取 NAME: t.CTypedef = Type 中的名称和类型表达式字符串,
# 调用 register_ctypedef 注册到动态 CTypedef 表。
# ============================================================
def _register_ctypedef_from_annassign(pool: memhub.MemBuddy | t.CPtr,
aa: ast.AnnAssign | t.CPtr) -> None:
"""从 AnnAssign 节点提取并注册 CTypedef"""
if aa is None or pool is None:
return
# 提取目标名称(必须是 Name 节点)
if aa.target is None or aa.target.kind() != ast.ASTKind.Name:
return
tgt: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.target)
if tgt.id is None:
return
# 提取类型表达式字符串(从 value AST 节点)
if aa.value is None:
return
type_str: str = GenApi._get_type_name_from_annotation(pool, aa.value)
if type_str is None:
return
# 注册 CTypedef
GenApi.register_ctypedef(pool, tgt.id, type_str)