2109 lines
108 KiB
Plaintext
2109 lines
108 KiB
Plaintext
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)
|