fix(vpireader): declare 参数注解改字符串解析优先,修复 t.CSizeT→i32
SlotsBugTest 根因:跨模块 declare 参数类型经 AST resolve_annotation_type 解析,vpi tree 深层节点 kind() 损坏时得垃圾类型(t.CSizeT→i32),而定义侧是 i64——调用侧 trunc i64→i32 传参、callee 按 i64 读栈残留垃圾高位 → can=巨值读未写槽位(0xCDCD)崩溃(ASAN node=0xCDCD,rcx 垃圾高 32 位实锤)。
修复:_inject_function_declare/_inject_method_declare 参数解析与返回类型同源——优先用 vpi 文本注解缓存(_resolve_vpi_param_annot_seg 按段索引解析),AST 仅兜底。map_t_type('CSizeT')=i64 字符串路径正确,can declare 恢复 i64,调用侧不再 trunc。
This commit is contained in:
1 parent
936aa14041
commit
4d14eeff67
1 file changed
+551
-12
@@ -40,6 +40,29 @@ _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 文本直接提取方法返回注解
|
||||
@@ -188,6 +211,367 @@ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
|
||||
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 - 扩容方法返回注解缓存(容量翻倍)
|
||||
#
|
||||
@@ -277,6 +661,66 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int:
|
||||
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
|
||||
#
|
||||
@@ -811,6 +1255,9 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
|
||||
# (不依赖 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
|
||||
@@ -818,7 +1265,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
|
||||
if src_sha1 is not None:
|
||||
stdlib.free(src_sha1)
|
||||
return 1
|
||||
n: t.CSizeT = ch.__len__()
|
||||
n: t.CSizeT = len(ch)
|
||||
|
||||
# 收集 stub 内被读模块自己的 import 语句,构建独立的注解解析上下文。
|
||||
# .vpi stub 顶部含被读模块的 import(Converter 已写入,如 string.vp 的
|
||||
@@ -835,7 +1282,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
|
||||
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 = simp.names.__len__()
|
||||
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))
|
||||
@@ -852,7 +1299,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
|
||||
stub_imported_modules = _stub_add_imported(
|
||||
pool, stub_imported_modules, sifr_mod)
|
||||
if sifr.names is not None:
|
||||
sifr_nn: t.CSizeT = sifr.names.__len__()
|
||||
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))
|
||||
@@ -959,11 +1406,11 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
|
||||
# 具体特化的方法 define 已在 Phase1 生成,跨模块调用按特化类名
|
||||
# (ndarray[t.CDouble].__add__)查找命中,无需 stub 模板 declare。
|
||||
tp_stub: list[str] | t.CPtr = class_cd.type_params
|
||||
if tp_stub is not None and tp_stub.__len__() > 0:
|
||||
if tp_stub is not None and len(tp_stub) > 0:
|
||||
continue
|
||||
cls_children: list[ast.AST | t.CPtr] | t.CPtr = class_cd.children
|
||||
if cls_children is not None:
|
||||
cn2: t.CSizeT = cls_children.__len__()
|
||||
cn2: t.CSizeT = len(cls_children)
|
||||
for ci2 in range(cn2):
|
||||
m_child: ast.AST | t.CPtr = cls_children.get(ci2)
|
||||
if m_child is None:
|
||||
@@ -1142,11 +1589,13 @@ def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
# 提取参数并添加到 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 = args_list.__len__()
|
||||
param_count = len(args_list)
|
||||
# 参数数上限哨兵(fail-fast):vpi tree 深层节点在遍历处理过程中
|
||||
# 可能被覆盖损坏(见本文件顶部注释),fd.args 悬空时 __len__()
|
||||
# 返回垃圾大数 → for 循环 add_param 海量次 → Params 链超长,
|
||||
@@ -1166,7 +1615,22 @@ def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
pname = "arg"
|
||||
# 解析参数类型
|
||||
param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty
|
||||
if ag.annotation is not None:
|
||||
# 优先用 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:
|
||||
@@ -1183,18 +1647,46 @@ def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
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,
|
||||
None, 0, param_count) == 0:
|
||||
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)
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -1269,11 +1761,13 @@ def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
# 提取参数并添加到 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 = args_list.__len__()
|
||||
param_count = len(args_list)
|
||||
# 参数数上限哨兵(fail-fast):与 _inject_function_declare 同因
|
||||
# (vpi tree 深层节点被覆盖损坏时 __len__() 返回垃圾大数)。
|
||||
if param_count < 0 or param_count > 128:
|
||||
@@ -1301,7 +1795,22 @@ def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
llvmlite.add_param(pool, func, llvmlite.Ptr(pool, cls_ty_sd), pname)
|
||||
continue
|
||||
param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty
|
||||
if ag.annotation is not None:
|
||||
# 优先用 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:
|
||||
@@ -1319,6 +1828,13 @@ def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
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)。
|
||||
@@ -1328,10 +1844,28 @@ def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
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 = args.__len__() - 1 保持一致)——默认参数填充按非 self
|
||||
# md_param_count = len(args) - 1 保持一致)——默认参数填充按非 self
|
||||
# 参数计数,若含 self 会导致 mdf_param_count 虚高、填充逻辑错位/越界。
|
||||
if funcs_ptr is not None:
|
||||
cur_count: int = trans._func_count
|
||||
@@ -1339,8 +1873,13 @@ def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
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,
|
||||
None, 0, param_count_no_self) == 0:
|
||||
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)
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
Reference in new issue
Block a user