_translate_method_call ignored cl.keywords, so keyword-style calls
like add_argument("--count", short="-c", arg_type=INT) lost all
keyword values (short_name/arg_type stayed default), breaking
Test 4. Match keywords against function params by name, fill
missing params with zero values to keep call arity consistent.'
886 lines
41 KiB
Plaintext
886 lines
41 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import string
|
||
import stdlib
|
||
import stdio
|
||
import sys
|
||
import memhub
|
||
import llvmlite
|
||
import viperlib
|
||
import w32.fileio as fileio
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.Handles.HandlesType as HandlesType
|
||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||
import lib.core.Handles.HandlesFunctions as HandlesFunctions
|
||
import lib.core.Handles.HandlesTranslator as HT
|
||
|
||
|
||
# ============================================================
|
||
# 方法返回注解缓存(stdlib 持久)
|
||
#
|
||
# 背景:vpi tree 在"解析后立即"有效,但遍历处理到后续类的方法时,
|
||
# 深层 AST 节点(如 parse_args 的 returns 注解)可能已被覆盖损坏
|
||
# (kind() 读取异常),直接 resolve 得到垃圾类型(如 i8*)。
|
||
# 因此在 tree 解析后立即把每个方法的返回注解转成字符串缓存到 stdlib
|
||
# (持久,不受 pool 状态影响),_inject_method_declare 用 _parse_type_string
|
||
# 重新解析缓存字符串(新 AST 节点,立即使用不被覆盖)。
|
||
# ============================================================
|
||
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
|
||
|
||
|
||
# ============================================================
|
||
# _cache_vpi_return_annots - tree 解析后立即缓存方法的返回注解字符串
|
||
# ============================================================
|
||
def _cache_vpi_return_annots(pool: memhub.MemBuddy | t.CPtr,
|
||
tree: ast.AST | t.CPtr,
|
||
src_sha1: str) -> int:
|
||
"""缓存 vpi tree 中所有 FunctionDef 的返回注解字符串(stdlib 持久)"""
|
||
global _g_vpi_ret_keys, _g_vpi_ret_strs, _g_vpi_ret_count
|
||
if tree 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)
|
||
sha1_len: t.CSizeT = string.strlen(src_sha1)
|
||
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
|
||
if ch is None:
|
||
return 0
|
||
n: t.CSizeT = ch.__len__()
|
||
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()
|
||
if kd == ast.ASTKind.ClassDef:
|
||
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
|
||
if cd is None or cd.name is None:
|
||
continue
|
||
mch: list[ast.AST | t.CPtr] | t.CPtr = cd.children
|
||
if mch is None:
|
||
continue
|
||
mn: t.CSizeT = mch.__len__()
|
||
mi: t.CSizeT
|
||
for mi in range(mn):
|
||
m_child: ast.AST | t.CPtr = mch.get(mi)
|
||
if m_child is None or m_child.kind() != ast.ASTKind.FunctionDef:
|
||
continue
|
||
m_fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m_child)
|
||
if m_fd is None or m_fd.name is None:
|
||
continue
|
||
_store_vpi_ret_entry(src_sha1, sha1_len, cd.name, m_fd.name, m_fd.returns)
|
||
elif kd == ast.ASTKind.FunctionDef:
|
||
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
|
||
if fd is None or fd.name is None:
|
||
continue
|
||
_store_vpi_ret_entry(src_sha1, sha1_len, None, fd.name, fd.returns)
|
||
return 1
|
||
|
||
|
||
def _store_vpi_ret_entry(src_sha1: str, sha1_len: t.CSizeT,
|
||
cls_name: str, method_name: str,
|
||
returns: ast.AST | t.CPtr) -> None:
|
||
"""存储单个方法的返回注解字符串到缓存"""
|
||
global _g_vpi_ret_count
|
||
if method_name is None:
|
||
return
|
||
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
|
||
if key_len >= VPI_KEY_LEN:
|
||
return
|
||
key_buf: bytes = stdlib.malloc(key_len + 1)
|
||
if key_buf is None:
|
||
return
|
||
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)
|
||
ret_str: str = None
|
||
if returns is not None:
|
||
ret_str = HandlesType.annotation_to_str(returns)
|
||
if ret_str is not None and string.strlen(ret_str) > 0:
|
||
if string.strlen(ret_str) < VPI_RET_LEN:
|
||
if _g_vpi_ret_count < VPI_RET_MAX:
|
||
string.strcpy(_g_vpi_ret_keys + _g_vpi_ret_count * VPI_KEY_LEN, key_buf)
|
||
string.strcpy(_g_vpi_ret_strs + _g_vpi_ret_count * VPI_RET_LEN, ret_str)
|
||
_g_vpi_ret_count += 1
|
||
stdlib.free(key_buf)
|
||
|
||
|
||
# ============================================================
|
||
# _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
|
||
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)
|
||
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
|
||
while cend < le 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:
|
||
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 arrow_pos < le and dend > dstart:
|
||
# 返回类型 = arrow_pos+2 跳过空格 到 ':' 前
|
||
rstart: t.CSizeT = arrow_pos + 2
|
||
while rstart < le and vpi_buf[rstart] == ' ':
|
||
rstart += 1
|
||
rend: t.CSizeT = rstart
|
||
while rend < le and vpi_buf[rend] != ':':
|
||
rend += 1
|
||
if rend > rstart:
|
||
# 构造 key 和 ret_str,存入缓存
|
||
mname_len: t.CSizeT = dend - dstart
|
||
rtype_len: t.CSizeT = rend - rstart
|
||
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'
|
||
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
|
||
if dup_key == 0 and _g_vpi_ret_count < VPI_RET_MAX:
|
||
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,
|
||
vpi_buf + rstart, 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
|
||
|
||
|
||
# ============================================================
|
||
# _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
|
||
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)
|
||
# t.XXX / c.XXX 内置类型映射
|
||
if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0:
|
||
attr_nm: str = seg + 2
|
||
mapped_ty: llvmlite.LLVMType | t.CPtr = HandlesType.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 = HandlesStruct.get_struct_type(cls_nm2)
|
||
if mod_struct_ty is not None:
|
||
return mod_struct_ty
|
||
return None
|
||
# 裸类名 → 全局 struct 注册表
|
||
bare_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(seg)
|
||
if bare_struct_ty is not None:
|
||
return bare_struct_ty
|
||
# 裸 typedef 名(如 VOIDPTR)
|
||
mapped_bare: llvmlite.LLVMType | t.CPtr = HandlesType.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
|
||
|
||
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 first_ty is not None:
|
||
return first_ty
|
||
return None
|
||
|
||
|
||
# ============================================================
|
||
# _ddebug_exit - DDEBUG 拦截退出辅助函数
|
||
#
|
||
# 刷新 stderr 缓冲区并通过 VLogger 输出可见消息后退出。
|
||
# 解决 sys.exit(1) 不刷新缓冲区导致 DDEBUG 消息丢失的问题。
|
||
# ============================================================
|
||
def _ddebug_exit() -> None:
|
||
stdio.fflush(stdio.stderr)
|
||
VLogger.error("DDEBUG intercept triggered - check stderr for details")
|
||
|
||
|
||
# ============================================================
|
||
# 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'
|
||
# DDEBUG: 检测从路径提取的 SHA1 是否带尾随点(>=17 字符),发现问题立即终止
|
||
_ext_sl: t.CSizeT = string.strlen(sha1_buf)
|
||
if _ext_sl >= 17:
|
||
stdio.printf("[DDEBUG] _extract_sha1_from_path too long: len=%d sha1='%s' path='%s'\n", _ext_sl, sha1_buf, vpi_path)
|
||
_ddebug_exit()
|
||
return sha1_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 文件,将函数声明注入到当前模块"""
|
||
if pool is None or mod is None or vpi_path is None or trans is None:
|
||
return 0
|
||
|
||
# 获取当前翻译器的导入信息(供 resolve_annotation_type 使用)
|
||
imported_modules: str = trans._imported_modules
|
||
from_imports: str = trans._from_imports
|
||
|
||
# 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)
|
||
|
||
# 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 = ch.__len__()
|
||
|
||
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: HandlesExprCall.FuncEntry | t.CPtr = trans._funcs
|
||
max_funcs: int = 256
|
||
|
||
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
|
||
if kd == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(child)
|
||
if aa is None:
|
||
continue
|
||
if HandlesType._is_ctypedef_annotation(aa.annotation) != 0:
|
||
_register_ctypedef_from_annassign(pool, aa)
|
||
continue
|
||
|
||
# ClassDef → 递归注入类内方法(如 MemBuddy.__init__/alloc)
|
||
# 方法 declare 使用 {sha1}.{ClassName}.{method} 混淆名,
|
||
# 参数类型从 vpi 注解解析(避免调用时 _ensure_method_declare 用实参类型
|
||
# 创建错误 declare,如 size: t.CSizeT 被写成 i32 而非 i64)
|
||
if kd == ast.ASTKind.ClassDef:
|
||
class_cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
|
||
if class_cd is None or class_cd.name is None:
|
||
continue
|
||
cls_name_v: str = class_cd.name
|
||
cls_children: list[ast.AST | t.CPtr] | t.CPtr = class_cd.children
|
||
if cls_children is not None:
|
||
cn2: t.CSizeT = cls_children.__len__()
|
||
for ci2 in range(cn2):
|
||
m_child: ast.AST | t.CPtr = cls_children.get(ci2)
|
||
if m_child is None:
|
||
continue
|
||
if m_child.kind() != ast.ASTKind.FunctionDef:
|
||
continue
|
||
m_fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m_child)
|
||
if m_fd is None or m_fd.name is None:
|
||
continue
|
||
_inject_method_declare(pool, mod, m_fd, cls_name_v, trans,
|
||
src_sha1, imported_modules, from_imports,
|
||
i32_ty, i8_ptr_ty, funcs_ptr, max_funcs)
|
||
continue
|
||
|
||
# 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_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: HandlesExprCall.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 = HandlesType.has_decorator_marker(fd.returns, "CExtern")
|
||
has_state = HandlesType.has_decorator_marker(fd.returns, "State")
|
||
has_export = HandlesType.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 = HandlesFunctions._mangle_name_with_sha1(src_sha1, fd.name)
|
||
|
||
# DDEBUG: VpiReader 是注入模块级函数 declare 的路径(无拦截,最可疑双点源头)
|
||
# 检测 src_sha1 带尾随点 / fd.name 带前导点 / mangled_name 含双点,发现立即终止
|
||
if mangled_name is not None and src_sha1 is not None and fd.name is not None:
|
||
_vpi_sl: t.CSizeT = string.strlen(src_sha1)
|
||
if _vpi_sl >= 17:
|
||
stdio.printf("[DDEBUG] VpiReader src_sha1 too long: len=%d sha1='%s' func='%s'\n", _vpi_sl, src_sha1, fd.name)
|
||
_ddebug_exit()
|
||
_vpi_fn_len: t.CSizeT = string.strlen(fd.name)
|
||
if _vpi_fn_len > 0 and fd.name[0] == '.':
|
||
stdio.printf("[DDEBUG] VpiReader fd.name starts with dot: name='%s' sha1='%s'\n", fd.name, src_sha1)
|
||
_ddebug_exit()
|
||
_vpi_mi: int = 0
|
||
_vpi_ml: t.CSizeT = string.strlen(mangled_name)
|
||
while _vpi_mi < _vpi_ml - 1:
|
||
if mangled_name[_vpi_mi] == 46 and mangled_name[_vpi_mi + 1] == 46:
|
||
stdio.printf("[DDEBUG] VpiReader mangled double-dot: sha1='%s' func='%s' mangled='%s'\n", src_sha1, fd.name, mangled_name)
|
||
_ddebug_exit()
|
||
_vpi_mi += 1
|
||
|
||
# 去重:如果函数已存在则跳过
|
||
existing: llvmlite.Function | t.CPtr = HandlesExprCall.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 = HandlesExprCall.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 = HandlesType.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
|
||
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__()
|
||
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
|
||
if ag.annotation is not None:
|
||
resolved_ty: llvmlite.LLVMType | t.CPtr = HandlesType.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 兼容。
|
||
match resolved_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
param_ty = llvmlite.Int64(pool)
|
||
case _:
|
||
param_ty = resolved_ty
|
||
llvmlite.add_param(pool, func, param_ty, pname)
|
||
# *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
|
||
|
||
# 注册到函数表(用裸名 fd.name,不是 mangled_name)
|
||
if funcs_ptr is not None:
|
||
cur_count: int = trans._func_count
|
||
if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs,
|
||
None, 0, param_count) == 0:
|
||
trans._func_count = cur_count + 1
|
||
|
||
|
||
# ============================================================
|
||
# _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: HandlesExprCall.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 = HandlesExprCall.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 = HandlesType.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 = HandlesStruct.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
|
||
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__()
|
||
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
|
||
if ag.annotation is not None:
|
||
resolved_ty: llvmlite.LLVMType | t.CPtr = HandlesType.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 兼容。
|
||
match resolved_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
param_ty = llvmlite.Int64(pool)
|
||
case _:
|
||
param_ty = resolved_ty
|
||
llvmlite.add_param(pool, func, param_ty, pname)
|
||
|
||
# 注册到函数表(用裸名 ClassName.method,与调用方 lookup_name 一致)
|
||
if funcs_ptr is not None:
|
||
cur_count: int = trans._func_count
|
||
if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, lookup_name, func, max_funcs,
|
||
None, 0, param_count) == 0:
|
||
trans._func_count = cur_count + 1
|
||
|
||
|
||
# ============================================================
|
||
# _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 = HandlesType._get_type_name_from_annotation(pool, aa.value)
|
||
if type_str is None:
|
||
return
|
||
# 注册 CTypedef
|
||
HandlesType.register_ctypedef(pool, tgt.id, type_str)
|