- StubMergerStore 新增共享导入上下文记账(AppendImportedModule / AppendFromImport / ImportCtx / AllocImportCtx / NotePlainImport),ImportsGenMixin 与 VpiReader 共用同一份 口径;顺带移除 add_from_import 的 256 缓冲静默截断,VpiReader 删除两份私有副本 - 注入窗口按「名字归属」判据解析(is_module_class + CurDefModuleSha1): _forward_declare_ptr_from_node 的 Name 分支、resolve_annotation_type 的 Name/Attribute 分支均补齐,使同一 stub 内先声明/先引用的类产出**具名占位**而不是 Ptr(i8),并新增 _is_tattr_marker 让 t.attr.* 属性标记不参与类型联合竞争 - VpiReader._inject_class_struct_declare:命中前向声明占位时不再早退,改为「占位 Ty 原地写入」(对齐 ClassGenDef.fill_fwd),修 StructTable 爆表(table full, cannot register Translator) - 清除全部一次性探针:[DBG-GEN](VpiReader / StructGenMixin / ClassGenDef)、 [DBG-ATTR](ExprGenAttr)、[MODSHA1](CallGenMixin) 根因记录:vpi 注入期漏记 `import X as Y -> from_imports "Y:X"`,使 GenTypes.VarEntry.Spec (TSpec.TypeSpec | t.CPtr)取不到模块 SHA1、退化为 i8* 并被永久冻结 (CallGenCtor.vp:897 .PtrLevel 属性访问失败)。
4681 lines
247 KiB
Plaintext
4681 lines
247 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.Gen.Translator as HT
|
||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||
import lib.Projectrans.Config as Config
|
||
|
||
|
||
# ============================================================
|
||
# REPL 存根布局恢复:常量与类表条目(实现见文件末同名段)
|
||
#
|
||
# 定义前置(先于 `_inject_class_struct_declare` 的 REPL 钩子使用):类型注解必须
|
||
# 在首次使用之前可见,故常量与条目类放在文件头部,函数体统一放在文件末。
|
||
# 该机制仅在 ReplStubLayoutMode != 0(REPL 会话初始化)时生效,批处理恒 0。
|
||
# ============================================================
|
||
|
||
_REPL_STUB_CLS_MAX: t.CDefine = 1024
|
||
_REPL_STUB_FIELD_MAX: int = 128
|
||
_REPL_STUB_VT_MAX: int = 128
|
||
_REPL_STUB_NAME_CAP: sizet = 256
|
||
_REPL_STUB_TXT_CAP: sizet = 4194304
|
||
_REPL_STUB_WARN_MAX: int = 10
|
||
_REPL_STUB_FINISH_PASSES: int = 8
|
||
|
||
# ReplStubClass.LayoutCount 的两个哨兵值(**不是字段数**)
|
||
_REPL_STUB_NOCOUNT_NOCLASS: int = -1 # 存根文本里没有该 key 的 `= type` 行(或整份存根没读到)
|
||
_REPL_STUB_NOCOUNT_OPAQUE: int = -2 # 存根里该 key 是 opaque(无 body)
|
||
|
||
# 布局"不可用 ⇒ 明确拒绝"的原因码(_repl_stub_warn_nodata):把"数据缺失"
|
||
# (上层数据没到位,补存根/重跑即可消除)与"判据拒绝"(数据在但不可信,绝不放行)
|
||
# 在文案与汇总计数上分开 —— 两类都会走到"不登记 StructEntry",但成因完全不同。
|
||
_REPL_SU_NOSTUB: t.CDefine = 1 # 数据缺失:整个模块没有 .stub.ll 文件
|
||
_REPL_SU_NOCLASS: t.CDefine = 2 # 判据拒绝:存根存在但无该类的 `= type` 行
|
||
_REPL_SU_OPAQUE: t.CDefine = 3 # 判据拒绝:存根里该类为 opaque(无 body)
|
||
_REPL_SU_PARENT: t.CDefine = 4 # 上游无数据:父类布局不可用/未在会话注册(走 _repl_stub_warn_parent)
|
||
|
||
# ReplStubClass.Flags / MethodFlags 位
|
||
_REPL_SF_CVTABLE: t.CDefine = 1
|
||
_REPL_SF_NOVTABLE: t.CDefine = 2
|
||
_REPL_SF_ANYFUNC: t.CDefine = 4
|
||
_REPL_SF_PACKED: t.CDefine = 8
|
||
|
||
# ReplStubClass.State
|
||
_REPL_SS_REFUSED: t.CDefine = 0 # 布局不可信:永不登记(字段访问明确失败)
|
||
_REPL_SS_OK: t.CDefine = 1 # 完整布局已登记
|
||
_REPL_SS_PENDING: t.CDefine = 2 # 父类尚未就绪:留待收尾遍
|
||
_REPL_SS_EMPTY: t.CDefine = 3 # 无父类/无虚表/字段类型不可解析:交既有 vpi 注入口径
|
||
|
||
class ReplStubClass:
|
||
Key: str # "<sha1>.<类名>"(= 定义侧命名结构体名)
|
||
ClassName: str
|
||
Sha1: str
|
||
Flags: int
|
||
OwnNames: sizet* # 元素 = str 指针(本类字段名,AnnAssign 顺序)
|
||
OwnTypes: sizet* # 元素 = LLVMType 指针
|
||
OwnCount: int
|
||
MethodNames: sizet*
|
||
MethodFlags: sizet*
|
||
MethodCount: int
|
||
Bases: sizet*
|
||
BaseCount: int
|
||
LayoutCount: int # 存根布局字段数(>=0 = 命中该类的 `= type {…}`;负数 = **哨兵,不是字段数**,
|
||
# 见 _REPL_STUB_NOCOUNT_NOCLASS/-OPAQUE:存根里没这个类 / 没读到存根 / opaque)
|
||
VtArity: int # 存根 .vtable 全局槽数(0 = 存根无该全局)
|
||
Names: sizet*
|
||
Types: sizet*
|
||
Count: int
|
||
HasVt: int
|
||
IsNoVTable: int
|
||
IsOOP: int
|
||
HasInit: int
|
||
HasNew: int
|
||
VtNames: sizet*
|
||
VtCount: int
|
||
VtTrust: int # 1 = 虚表槽数已与存根一致(可登记 VTableMethods)
|
||
OwnFail: int # 1 = 本类有字段注解解析失败 ⇒ OwnCount 只是"已解析字段数"(不完整)
|
||
State: int
|
||
|
||
|
||
# ============================================================
|
||
# 方法返回注解缓存(stdlib 持久)
|
||
#
|
||
# vpi tree 仅在解析后立即有效:处理到后续类的方法时深层 AST 节点(如 returns 注解)可能已被覆盖损坏(kind() 异常)→ 直接 resolve 得到垃圾类型。
|
||
# 故改文本提取: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 节点(含函数参数列表)可能被覆盖损坏 → args 被截断(带默认值的尾部参数丢失,同一 vpi 不同模块注入时忽对忽错)→ declare 参数数不足 →
|
||
# 调用点缺参(fill 无法超 declare 补)→ 被调函数按 x64 ABI 读残留寄存器/栈槽垃圾。故与返回注解同源:从 vpi_buf 原始文本按行提取参数注解串缓存,
|
||
# 注入 declare 时若 tree 参数数不足则用缓存补全,保证 declare 与 define 参数数一致。
|
||
# ============================================================
|
||
VPI_PARAM_MAX: t.CDefine = 4096
|
||
VPI_PARAM_KEY_LEN: t.CDefine = 224
|
||
VPI_PARAM_LEN: t.CDefine = 1024
|
||
|
||
# ============================================================
|
||
# 键表开放寻址索引(名 → 槽下标)
|
||
#
|
||
# 两张键表(返回注解 / 参数注解)都是定长槽数组(每槽 VPI_*_KEY_LEN 字节),
|
||
# 插入只追加、从不重排 ⇒ 索引可只存"槽下标"(比存指针更省,且 realloc 移动
|
||
# 键表基址后槽下标仍有效)。索引槽数恒为键表容量的 2 倍(2 的幂,负载 < 0.5,
|
||
# 线性探测必然终止)。每次注册占一槽(不按键去重):同键条目沿探测链按注册
|
||
# 顺序递增排列,查找取链上首个"当前键相等"者 = 原线性扫描首个命中(首注册
|
||
# 优先)。可信判据 = Built == Count;不可信/未命中一律回退原线性扫描。
|
||
# ============================================================
|
||
VPI_RET_IDX_CAP: t.CDefine = 8192
|
||
VPI_PARAM_IDX_CAP: t.CDefine = 8192
|
||
# 索引哈希常量(32 位 FNV-1a,与 StructGenMixin 范式一致)
|
||
VPI_IDX_FNV_OFFSET: t.CDefine = 2166136261
|
||
VPI_IDX_FNV_PRIME: t.CDefine = 16777619
|
||
VPI_IDX_FNV_MASK: t.CDefine = 4294967295
|
||
|
||
_g_vpi_param_keys: bytes = None
|
||
_g_vpi_param_strs: bytes = None
|
||
_g_vpi_param_count: int = 0
|
||
_g_vpi_param_cap: int = 0
|
||
|
||
# 返回注解键表索引(与键表同 malloc 生命周期;Built == Count 才可信)
|
||
_g_vpi_ret_idx: bytes = None
|
||
_g_vpi_ret_idx_cap: int = 0
|
||
_g_vpi_ret_idx_built: int = 0
|
||
# 参数注解键表索引(同上)
|
||
_g_vpi_param_idx: bytes = None
|
||
_g_vpi_param_idx_cap: int = 0
|
||
_g_vpi_param_idx_built: int = 0
|
||
|
||
|
||
# ============================================================
|
||
# 索引探测/插入/重建(模块级函数,供两张键表共用)
|
||
# ============================================================
|
||
def _vpi_idx_hash(key: bytes) -> uint64:
|
||
"""键串 → 64 位哈希(32 位 FNV-1a,逐字节到 NUL 结束;空键返回偏移基值)"""
|
||
h: uint64 = uint64(VPI_IDX_FNV_OFFSET)
|
||
if key is None:
|
||
return h
|
||
i: sizet = 0
|
||
while key[i] != '\0':
|
||
h = (h ^ uint64(int(key[i]))) * uint64(VPI_IDX_FNV_PRIME)
|
||
h = h & uint64(VPI_IDX_FNV_MASK)
|
||
i += 1
|
||
return h
|
||
|
||
|
||
def _vpi_idx_slot(idx_base: bytes, si: sizet) -> sizet*:
|
||
"""取索引第 si 个槽(8 字节,存 slot_index+1;0=空槽)"""
|
||
return (sizet*)(uint64(idx_base) + uint64(si) * 8)
|
||
|
||
|
||
def _vpi_idx_find(keys: bytes, key_stride: int, idx_base: bytes,
|
||
cap: int, key: bytes) -> int:
|
||
"""在索引中探测 key,返回命中槽下标(>=0);未命中/索引不可用返回 -1
|
||
|
||
命中判据是"当前键相等"(strcmp),不能只比哈希。
|
||
"""
|
||
if keys is None or idx_base is None or cap <= 0 or key is None:
|
||
return -1
|
||
h: uint64 = _vpi_idx_hash(key)
|
||
mask: sizet = sizet(cap - 1)
|
||
si: sizet = sizet(h) & mask
|
||
i: int = 0
|
||
while i < cap:
|
||
slot: sizet* = _vpi_idx_slot(idx_base, si)
|
||
sv: sizet = slot[0]
|
||
if sv == 0:
|
||
return -1
|
||
se: int = int(sv) - 1
|
||
ek: bytes = keys + se * key_stride
|
||
if string.strcmp(ek, key) == 0:
|
||
return se
|
||
si = (si + 1) & mask
|
||
i += 1
|
||
return -1
|
||
|
||
|
||
def _vpi_idx_insert(idx_base: bytes, cap: int, slot_index: int, key: bytes) -> int:
|
||
"""把 (key → slot_index) 记入索引(每次注册占一槽,不去重);0=成功 / 1=失败"""
|
||
if idx_base is None or cap <= 0 or key is None:
|
||
return 1
|
||
h: uint64 = _vpi_idx_hash(key)
|
||
mask: sizet = sizet(cap - 1)
|
||
si: sizet = sizet(h) & mask
|
||
i: int = 0
|
||
while i < cap:
|
||
slot: sizet* = _vpi_idx_slot(idx_base, si)
|
||
if slot[0] == 0:
|
||
slot[0] = sizet(slot_index + 1)
|
||
return 0
|
||
si = (si + 1) & mask
|
||
i += 1
|
||
return 1
|
||
|
||
|
||
def _vpi_rebuild_ret_idx(new_cap: int) -> int:
|
||
"""随返回注解键表扩容重建索引(槽数=new_cap,2 的幂);0=可信 / 1=不可信
|
||
|
||
槽存下标非指针:键表 realloc 后仅基址变,槽下标与键内容均未变,故重建
|
||
只需从新基址逐条重放现有 count 个键即可(顺序不变 ⇒ 首注册优先不变)。
|
||
"""
|
||
global _g_vpi_ret_idx, _g_vpi_ret_idx_cap, _g_vpi_ret_idx_built
|
||
if new_cap <= 0:
|
||
_g_vpi_ret_idx = None
|
||
_g_vpi_ret_idx_cap = 0
|
||
_g_vpi_ret_idx_built = -1
|
||
return 1
|
||
new_idx: bytes = None
|
||
if _g_vpi_ret_idx is None:
|
||
new_idx = stdlib.malloc(new_cap * 8)
|
||
else:
|
||
new_idx = stdlib.realloc(_g_vpi_ret_idx, new_cap * 8)
|
||
if new_idx is None:
|
||
_g_vpi_ret_idx = None
|
||
_g_vpi_ret_idx_cap = 0
|
||
_g_vpi_ret_idx_built = -1
|
||
return 1
|
||
string.memset(new_idx, 0, sizet(new_cap) * 8)
|
||
_g_vpi_ret_idx = new_idx
|
||
_g_vpi_ret_idx_cap = new_cap
|
||
fail: int = 0
|
||
ri: int
|
||
for ri in range(_g_vpi_ret_count):
|
||
if _vpi_idx_insert(new_idx, new_cap, ri,
|
||
_g_vpi_ret_keys + ri * VPI_KEY_LEN) != 0:
|
||
fail = 1
|
||
break
|
||
if fail != 0:
|
||
_g_vpi_ret_idx_built = -1
|
||
return 1
|
||
_g_vpi_ret_idx_built = _g_vpi_ret_count
|
||
return 0
|
||
|
||
|
||
def _vpi_rebuild_param_idx(new_cap: int) -> int:
|
||
"""随参数注解键表扩容重建索引(同 _vpi_rebuild_ret_idx);0=可信 / 1=不可信"""
|
||
global _g_vpi_param_idx, _g_vpi_param_idx_cap, _g_vpi_param_idx_built
|
||
if new_cap <= 0:
|
||
_g_vpi_param_idx = None
|
||
_g_vpi_param_idx_cap = 0
|
||
_g_vpi_param_idx_built = -1
|
||
return 1
|
||
new_idx: bytes = None
|
||
if _g_vpi_param_idx is None:
|
||
new_idx = stdlib.malloc(new_cap * 8)
|
||
else:
|
||
new_idx = stdlib.realloc(_g_vpi_param_idx, new_cap * 8)
|
||
if new_idx is None:
|
||
_g_vpi_param_idx = None
|
||
_g_vpi_param_idx_cap = 0
|
||
_g_vpi_param_idx_built = -1
|
||
return 1
|
||
string.memset(new_idx, 0, sizet(new_cap) * 8)
|
||
_g_vpi_param_idx = new_idx
|
||
_g_vpi_param_idx_cap = new_cap
|
||
fail: int = 0
|
||
ri: int
|
||
for ri in range(_g_vpi_param_count):
|
||
if _vpi_idx_insert(new_idx, new_cap, ri,
|
||
_g_vpi_param_keys + ri * VPI_PARAM_KEY_LEN) != 0:
|
||
fail = 1
|
||
break
|
||
if fail != 0:
|
||
_g_vpi_param_idx_built = -1
|
||
return 1
|
||
_g_vpi_param_idx_built = _g_vpi_param_count
|
||
return 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
|
||
global _g_vpi_ret_idx, _g_vpi_ret_idx_cap, _g_vpi_ret_idx_built
|
||
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
|
||
# 名索引:槽数 = 键表容量 * 2。分配失败则保持不可信(Built=-1),
|
||
# 查找/写去重一律回退原线性扫描(语义完全一致,不因此使缓存失败)。
|
||
_g_vpi_ret_idx = stdlib.malloc(VPI_RET_IDX_CAP * 8)
|
||
if _g_vpi_ret_idx is not None:
|
||
string.memset(_g_vpi_ret_idx, 0, VPI_RET_IDX_CAP * 8)
|
||
_g_vpi_ret_idx_cap = VPI_RET_IDX_CAP
|
||
_g_vpi_ret_idx_built = _g_vpi_ret_count
|
||
else:
|
||
_g_vpi_ret_idx_cap = 0
|
||
_g_vpi_ret_idx_built = -1
|
||
sha1_len: sizet = string.strlen(src_sha1)
|
||
buf_len: sizet = string.strlen(vpi_buf)
|
||
cur_cls: bytes = None
|
||
pos: sizet = 0
|
||
line_no: int = 0
|
||
while pos < buf_len:
|
||
ls: sizet = pos
|
||
le: sizet = pos
|
||
while le < buf_len and vpi_buf[le] != '\n':
|
||
le += 1
|
||
line_len: sizet = le - ls
|
||
# 跳过前导空格,检查 class/def
|
||
i: sizet = 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: sizet = i + 6
|
||
cend: sizet = 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: sizet = i + 4
|
||
dend: sizet = dstart
|
||
while dend < le and vpi_buf[dend] != '(':
|
||
dend += 1
|
||
# 方法名 = [dstart, dend)
|
||
# 找 "->"
|
||
rpos: sizet = dstart
|
||
arrow_pos: sizet = 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: sizet = 0
|
||
if arrow_pos < le:
|
||
# 返回类型 = arrow_pos+2 跳过空格 到 ':' 前
|
||
rstart2: sizet = arrow_pos + 2
|
||
while rstart2 < le and vpi_buf[rstart2] == ' ':
|
||
rstart2 += 1
|
||
rend2: sizet = 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: sizet = dend - dstart
|
||
cls_len2: sizet = 0
|
||
if cur_cls is not None:
|
||
cls_len2 = string.strlen(cur_cls)
|
||
key_len: sizet = 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
|
||
# 索引快速路径:索引可信(Built==Count)时一次探测判定
|
||
# 重复(命中即"首个注册优先"= 原线性扫描首个同名);
|
||
# 未命中 | 索引不可信 → 回退原线性扫描(正确性保证)。
|
||
if _g_vpi_ret_idx_built == _g_vpi_ret_count \
|
||
and _g_vpi_ret_idx is not None and _g_vpi_ret_idx_cap > 0:
|
||
if _vpi_idx_find(_g_vpi_ret_keys, VPI_KEY_LEN,
|
||
_g_vpi_ret_idx, _g_vpi_ret_idx_cap, key_buf) >= 0:
|
||
dup_key = 1
|
||
else:
|
||
# 快速路径:重复键必首字节相同(strcmp 判等蕴含首字节
|
||
# 相等),键首字节 = src_sha1[0],首字节不同者跳过 strcmp。
|
||
dup_first: int = key_buf[0]
|
||
for dki in range(_g_vpi_ret_count):
|
||
ent_d: bytes = _g_vpi_ret_keys + dki * VPI_KEY_LEN
|
||
if ent_d[0] == dup_first and string.strcmp(ent_d, 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
|
||
# 同步索引:仅当插入前可信(Built==Count-1,含扩容
|
||
# 重建后)且插入成功;否则标记不可信(查找回退扫描)。
|
||
if _g_vpi_ret_idx_built == _g_vpi_ret_count - 1 \
|
||
and _vpi_idx_insert(_g_vpi_ret_idx, _g_vpi_ret_idx_cap,
|
||
_g_vpi_ret_count - 1, key_buf) == 0:
|
||
_g_vpi_ret_idx_built = _g_vpi_ret_count
|
||
else:
|
||
_g_vpi_ret_idx_built = -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 文本提取函数/方法参数注解串
|
||
#
|
||
# vpi tree 深层节点在遍历中可能被覆盖损坏(参数列表截断),故从原始文本按行提取参数注解,以 '@' 分隔存储(含首参 self → 空串占位,保证「参数位置 == 段索引」;注解文法不含 '@')。
|
||
# 存储格式:key="sha1.ClassName.method"(类内)/"sha1.method"(模块级),val=各参数注解串 '@' 分隔(无注解的参数为空串)。
|
||
# 调用方: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
|
||
global _g_vpi_param_idx, _g_vpi_param_idx_cap, _g_vpi_param_idx_built
|
||
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
|
||
# 名索引:槽数 = 键表容量 * 2。分配失败则保持不可信(Built=-1),
|
||
# 查找/写去重一律回退原线性扫描(语义完全一致,不因此使缓存失败)。
|
||
_g_vpi_param_idx = stdlib.malloc(VPI_PARAM_IDX_CAP * 8)
|
||
if _g_vpi_param_idx is not None:
|
||
string.memset(_g_vpi_param_idx, 0, VPI_PARAM_IDX_CAP * 8)
|
||
_g_vpi_param_idx_cap = VPI_PARAM_IDX_CAP
|
||
_g_vpi_param_idx_built = _g_vpi_param_count
|
||
else:
|
||
_g_vpi_param_idx_cap = 0
|
||
_g_vpi_param_idx_built = -1
|
||
sha1_len: sizet = string.strlen(src_sha1)
|
||
buf_len: sizet = string.strlen(vpi_buf)
|
||
cur_cls: bytes = None
|
||
pos: sizet = 0
|
||
while pos < buf_len:
|
||
ls: sizet = pos
|
||
le: sizet = pos
|
||
while le < buf_len and vpi_buf[le] != '\n':
|
||
le += 1
|
||
line_len: sizet = le - ls
|
||
i: sizet = 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: sizet = i + 6
|
||
cend: sizet = 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: sizet = i + 4
|
||
dend: sizet = dstart
|
||
while dend < le and vpi_buf[dend] != '(':
|
||
dend += 1
|
||
if dend >= le:
|
||
pos = le + 1
|
||
continue
|
||
# 找匹配的 ')'(深度感知:注解可含 t.CArray[...] / tuple[...] 括号)
|
||
p: sizet = dend + 1
|
||
pdepth: int = 0
|
||
pend: sizet = dend
|
||
while p < le:
|
||
ch_p: char = 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: sizet = 0
|
||
seg_written: int = 0
|
||
seg_cur: sizet = 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: sizet = seg_cur
|
||
sd: int = 0
|
||
while seg_cur < pend:
|
||
ch_s: char = 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: sizet = seg_cur
|
||
# 跳过 * / **(vararg/kwarg 不入 args 列表)
|
||
st: sizet = 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: sizet = st
|
||
ed: int = 0
|
||
while e_pos < s_end:
|
||
ch_e: char = 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: sizet = st
|
||
cd2: int = 0
|
||
annot_start: sizet = s_end
|
||
while c_pos < s_end:
|
||
ch_c: char = 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: sizet = annot_start
|
||
while a_start < s_end and vpi_buf[a_start] == ' ':
|
||
a_start += 1
|
||
a_end: sizet = 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: sizet
|
||
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: sizet = dend - dstart
|
||
cls_len2: sizet = 0
|
||
if cur_cls is not None:
|
||
cls_len2 = string.strlen(cur_cls)
|
||
key_len: sizet = 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
|
||
# 索引快速路径:索引可信(Built==Count)时一次探测判定
|
||
# 重复(命中即"首个注册优先"= 原线性扫描首个同名);
|
||
# 未命中 | 索引不可信 → 回退原线性扫描(正确性保证)。
|
||
if _g_vpi_param_idx_built == _g_vpi_param_count \
|
||
and _g_vpi_param_idx is not None and _g_vpi_param_idx_cap > 0:
|
||
if _vpi_idx_find(_g_vpi_param_keys, VPI_PARAM_KEY_LEN,
|
||
_g_vpi_param_idx, _g_vpi_param_idx_cap, key_buf) >= 0:
|
||
dup_key = 1
|
||
else:
|
||
# 快速路径:重复键必首字节相同(strcmp 判等蕴含首字节
|
||
# 相等),键首字节 = src_sha1[0],首字节不同者跳过 strcmp。
|
||
dup_first: int = key_buf[0]
|
||
for dki in range(_g_vpi_param_count):
|
||
ent_d: bytes = _g_vpi_param_keys + dki * VPI_PARAM_KEY_LEN
|
||
if ent_d[0] == dup_first and string.strcmp(ent_d, 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
|
||
# 同步索引:仅当插入前可信(Built==Count-1,含扩容
|
||
# 重建后)且插入成功;否则标记不可信(查找回退扫描)。
|
||
if _g_vpi_param_idx_built == _g_vpi_param_count - 1 \
|
||
and _vpi_idx_insert(_g_vpi_param_idx, _g_vpi_param_idx_cap,
|
||
_g_vpi_param_count - 1, key_buf) == 0:
|
||
_g_vpi_param_idx_built = _g_vpi_param_count
|
||
else:
|
||
_g_vpi_param_idx_built = -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
|
||
# 索引随表同步扩容并整体重建(槽数 = 表容量 * 2,恒为 2 的幂)
|
||
_vpi_rebuild_param_idx(new_cap * 2)
|
||
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: sizet = string.strlen(src_sha1)
|
||
mlen: sizet = string.strlen(method_name)
|
||
clen: sizet = 0
|
||
if cls_name is not None:
|
||
clen = string.strlen(cls_name)
|
||
key_len: sizet = 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
|
||
# 索引快速路径:索引可信(Built==Count)时一次探测返回命中槽下标(= 原线性
|
||
# 扫描首个同名,首注册优先);未命中即"确无"(键表只追加、键文本注册后不改写,
|
||
# 索引随扩容整体重建且覆盖全表)⇒ 无需再线性扫描;索引不可信 → 回退线性扫描。
|
||
hit_p: int = -1
|
||
if _g_vpi_param_idx_built == _g_vpi_param_count \
|
||
and _g_vpi_param_idx is not None and _g_vpi_param_idx_cap > 0:
|
||
hit_p = _vpi_idx_find(_g_vpi_param_keys, VPI_PARAM_KEY_LEN,
|
||
_g_vpi_param_idx, _g_vpi_param_idx_cap, key_buf)
|
||
else:
|
||
# 回退原线性扫描:strcmp 判等蕴含首字节相等;键首字节 = src_sha1[0],缓存含
|
||
# 多模块条目,首字节不同者必不相等,直接跳过其 strcmp(仅做否定判定,不改命中)。
|
||
key_first: int = key_buf[0]
|
||
for ki in range(_g_vpi_param_count):
|
||
ent_p: bytes = _g_vpi_param_keys + ki * VPI_PARAM_KEY_LEN
|
||
if ent_p[0] == key_first and string.strcmp(ent_p, key_buf) == 0:
|
||
hit_p = ki
|
||
break
|
||
if hit_p >= 0:
|
||
r = _g_vpi_param_strs + hit_p * VPI_PARAM_LEN
|
||
stdlib.free(key_buf)
|
||
return r
|
||
|
||
|
||
# ============================================================
|
||
# _resolve_vpi_param_annot_seg_template - 泛型特化类的参数注解段解析
|
||
#
|
||
# 缓存键段取自 vpi 的 class 行文本(`class list[T]:` ⇒ 键段 "list[T]"),故特化类名(list[str]/list[int])的精确键必然遗漏,定义侧形参注解取不到 →
|
||
# 调用点 declare 停在调用点实参宽度(如 elem_size 字面量 i32 把定义侧 sizet(i64) 固化窄)。本函数按 {sha1}.{base}[T].{method} 形态定位模板条目,
|
||
# 把段内独立的类型形参 token 用特化实参(cls_name 的 [...] 内容)替换后再解析;未命中/多参模板(键段含空格被截断)返回 None,不做兜底推断。
|
||
# ============================================================
|
||
def _resolve_vpi_param_annot_seg_template(pool: memhub.MemBuddy*,
|
||
src_sha1: str,
|
||
cls_name: str,
|
||
method_name: str,
|
||
seg_idx: int,
|
||
i32_ty: llvmlite.LLVMType*,
|
||
i8_ptr_ty: llvmlite.LLVMType*) -> llvmlite.LLVMType*:
|
||
"""特化类 X[arg] 的参数注解段解析(模板 X[T] 注解 + T→arg 替换);未命中 None"""
|
||
if pool is None or src_sha1 is None or cls_name is None or method_name is None \
|
||
or _g_vpi_param_keys is None:
|
||
return None
|
||
lbkt_t: str = string.strchr(cls_name, 91) # '[' = 91
|
||
if lbkt_t is None:
|
||
return None
|
||
rbkt_t: str = string.strrchr(cls_name, 93) # ']' = 93
|
||
if rbkt_t is None or rbkt_t <= lbkt_t:
|
||
return None
|
||
base_len_t: sizet = uint64(lbkt_t) - uint64(cls_name)
|
||
arg_len_t: sizet = uint64(rbkt_t) - uint64(lbkt_t) - 1
|
||
if base_len_t <= 0 or arg_len_t <= 0:
|
||
return None
|
||
base_t: bytes = stdlib.malloc(base_len_t + 1)
|
||
if base_t is None:
|
||
return None
|
||
arg_t: bytes = stdlib.malloc(arg_len_t + 1)
|
||
if arg_t is None:
|
||
stdlib.free(base_t)
|
||
return None
|
||
string.strncpy(base_t, cls_name, base_len_t)
|
||
base_t[base_len_t] = '\0'
|
||
string.strncpy(arg_t, lbkt_t + 1, arg_len_t)
|
||
arg_t[arg_len_t] = '\0'
|
||
sha1_len_t: sizet = string.strlen(src_sha1)
|
||
ty_t: llvmlite.LLVMType* = None
|
||
ki_t: int
|
||
for ki_t in range(_g_vpi_param_count):
|
||
gk_t: bytes = _g_vpi_param_keys + ki_t * VPI_PARAM_KEY_LEN
|
||
if string.strncmp(gk_t, src_sha1, sha1_len_t) != 0 or gk_t[sha1_len_t] != '.':
|
||
continue
|
||
if string.strncmp(gk_t + sha1_len_t + 1, base_t, base_len_t) != 0:
|
||
continue
|
||
if gk_t[sha1_len_t + 1 + base_len_t] != 91:
|
||
continue
|
||
gklb_t: str = string.strchr(gk_t + sha1_len_t + 1 + base_len_t, 93)
|
||
if gklb_t is None:
|
||
continue
|
||
tplen_t: sizet = uint64(gklb_t) - uint64(gk_t + sha1_len_t + 1 + base_len_t) - 1
|
||
# 方法名须整体匹配(gklb_t 后即 ']' 与 '.';mangled 形态保证无同名歧义)
|
||
if tplen_t <= 0 or string.strcmp(gklb_t + 2, method_name) != 0:
|
||
continue
|
||
annots_t: str = _g_vpi_param_strs + ki_t * VPI_PARAM_LEN
|
||
if annots_t is None or annots_t[0] == '\0':
|
||
break
|
||
# 段定位(与 _resolve_vpi_param_annot_seg 同口径:'@' 分隔,段 0 = self 空段)
|
||
cur_t: sizet = 0
|
||
cur_seg_t: int = 0
|
||
while cur_seg_t < seg_idx and annots_t[cur_t] != '\0':
|
||
while annots_t[cur_t] != '\0' and annots_t[cur_t] != '@':
|
||
cur_t += 1
|
||
if annots_t[cur_t] == '@':
|
||
cur_t += 1
|
||
cur_seg_t += 1
|
||
seg_start_t: sizet = cur_t
|
||
seg_len_t: sizet = 0
|
||
while annots_t[seg_start_t + seg_len_t] != '\0' \
|
||
and annots_t[seg_start_t + seg_len_t] != '@':
|
||
seg_len_t += 1
|
||
if seg_len_t == 0:
|
||
break
|
||
seg_buf_t: bytes = stdlib.malloc(seg_len_t + 1)
|
||
if seg_buf_t is None:
|
||
break
|
||
string.strncpy(seg_buf_t, annots_t + seg_start_t, seg_len_t)
|
||
seg_buf_t[seg_len_t] = '\0'
|
||
# 类型形参 token(如 T)→ 特化实参(如 str / rawptr[AST] / AST|t.CPtr):
|
||
# 模板注解按源码原样缓存,不替换则 `item: T` 解析不出类型(None 跳过)
|
||
tparam_t: bytes = stdlib.malloc(tplen_t + 1)
|
||
if tparam_t is not None:
|
||
string.strncpy(tparam_t, gk_t + sha1_len_t + 1 + base_len_t + 1, tplen_t)
|
||
tparam_t[tplen_t] = '\0'
|
||
subst_t: str = _subst_template_arg(seg_buf_t, tparam_t, arg_t)
|
||
if subst_t is not None:
|
||
# _subst_template_arg 早退分支(tplen==0 或 tplen>alen)返回入参
|
||
# 本身 ⇒ subst_t 与 seg_buf_t 同指针时并非新分配,此处不得 free
|
||
# (否则与末尾 free(seg_buf_t) 重复释放)
|
||
if uint64(subst_t) != uint64(seg_buf_t):
|
||
stdlib.free(seg_buf_t)
|
||
seg_buf_t = subst_t
|
||
stdlib.free(tparam_t)
|
||
ty_t = _resolve_vpi_ret_annot_str(pool, seg_buf_t, i32_ty, i8_ptr_ty)
|
||
stdlib.free(seg_buf_t)
|
||
break
|
||
stdlib.free(base_t)
|
||
stdlib.free(arg_t)
|
||
return ty_t
|
||
|
||
|
||
# ============================================================
|
||
# _repair_declare_params_from_text - 用文本缓存补全 declare 参数
|
||
#
|
||
# vpi tree 参数列表被覆盖损坏时(param_count 偏小,尾部默认参数丢失),用 _cache_vpi_param_annots_text 的文本缓存补全:参数位置 p 对应缓存段 p(含首参空串占位)。
|
||
# 补全后 declare 参数数与 define 一致,调用点默认参数填充才能生效,避免被调函数读栈垃圾。
|
||
# ============================================================
|
||
def _repair_declare_params_from_text(pool: memhub.MemBuddy*,
|
||
func: llvmlite.Function*,
|
||
src_sha1: str,
|
||
cls_name: str,
|
||
method_name: str,
|
||
param_count: int,
|
||
i32_ty: llvmlite.LLVMType*,
|
||
i8_ptr_ty: llvmlite.LLVMType*) -> 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: sizet = 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: sizet = 0
|
||
cur2: sizet = 0
|
||
while prm_annots[cur2] != '\0':
|
||
while prm_annots[cur2] != '\0' and prm_annots[cur2] != '@':
|
||
cur2 += 1
|
||
seg_len2: sizet = cur2 - seg_start2
|
||
if prm_annots[cur2] == '@':
|
||
cur2 += 1
|
||
if seg_idx >= param_count:
|
||
param_ty_r: llvmlite.LLVMType* = 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* = _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() 损坏与 AST resolve_annotation_type 得到的垃圾类型,如 sizet → i32):
|
||
# 参数类型错会导致调用侧 trunc 传参、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*,
|
||
src_sha1: str,
|
||
cls_name: str,
|
||
method_name: str,
|
||
seg_idx: int,
|
||
i32_ty: llvmlite.LLVMType*,
|
||
i8_ptr_ty: llvmlite.LLVMType*) -> llvmlite.LLVMType*:
|
||
"""按段索引解析 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: sizet = 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: sizet = cur_p
|
||
seg_len_p: sizet = 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* = _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、每模块几十个方法),缓存写满后条目被静默丢弃会让已缓存模块的方法查不到而 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
|
||
# 索引随表同步扩容并整体重建(槽数 = 表容量 * 2,恒为 2 的幂)
|
||
_vpi_rebuild_ret_idx(new_cap * 2)
|
||
return 1
|
||
|
||
|
||
# ============================================================
|
||
# _ensure_vpi_ret_annot_loaded - 按需加载模块的 vpi stub 返回注解缓存
|
||
#
|
||
# _cache_vpi_return_annots_text 仅在 stub 注入(当前模块 import 了该模块)时填充;模块自身翻译时自己的 stub 尚未被依赖方读取、text.ll 也未落盘,
|
||
# 导致「同模块方法调用(定义在后)」在 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: sizet = 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
|
||
# 快速路径:前缀匹配必首两字节相等(strncmp 判等蕴含前缀逐字节相等),
|
||
# src_sha1 前两字节即模块子目录名;先比二者可跳过绝大多数条目的 strncmp。
|
||
s0: int = src_sha1[0]
|
||
s1: int = src_sha1[1]
|
||
for pre_i in range(_g_vpi_ret_count):
|
||
pre_key: bytes = _g_vpi_ret_keys + pre_i * VPI_KEY_LEN
|
||
if pre_key[0] == s0 and pre_key[1] == s1 and \
|
||
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: sizet = string.strlen(temp_dir)
|
||
path_len: sizet = 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: rawptr[fileio.File] = 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
|
||
_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 同构(缓存仅在 stub 注入时填充);跨模块方法调用可能绕过 stub 注入——如经 Generator 属性(self.Gen.ExprGenCdef.method)
|
||
# 调用依赖模块方法时依赖收集不加载其 vpi → 函数表无 ParamCount/默认参数 → 调用点缺参 → 被调函数读栈残留(IterTest 假递归根因)。
|
||
# 故参数缓存未命中时直接读 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: sizet = 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
|
||
# 快速路径:前缀匹配必首两字节相等(strncmp 判等蕴含前缀逐字节相等),
|
||
# src_sha1 前两字节即模块子目录名;先比二者可跳过绝大多数条目的 strncmp。
|
||
s0: int = src_sha1[0]
|
||
s1: int = src_sha1[1]
|
||
for pre_i in range(_g_vpi_param_count):
|
||
pre_key: bytes = _g_vpi_param_keys + pre_i * VPI_PARAM_KEY_LEN
|
||
if pre_key[0] == s0 and pre_key[1] == s1 and \
|
||
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: sizet = string.strlen(temp_dir)
|
||
path_len: sizet = 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: rawptr[fileio.File] = 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
|
||
_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)中的类型参数名需替换为实际特化实参后才能解析;仅替换前后为非字母数字下划线边界的独立 token(避免误替换 sizet 等)。
|
||
# 返回 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: sizet = string.strlen(annot)
|
||
tplen: sizet = string.strlen(tparam)
|
||
tglen: sizet = 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: sizet = alen / tplen
|
||
if max_sub == 0:
|
||
max_sub = 1
|
||
out_size: sizet = alen + max_sub * tglen + 1
|
||
out: bytes = stdlib.malloc(out_size)
|
||
if out is None:
|
||
return None
|
||
opos: sizet = 0
|
||
p: sizet = 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: sizet = string.strlen(src_sha1)
|
||
cl_len: sizet = 0
|
||
if cls_name is not None:
|
||
cl_len = string.strlen(cls_name)
|
||
me_len: sizet = string.strlen(method_name)
|
||
key_len: sizet = 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
|
||
# 索引快速路径:索引可信(Built==Count)时一次探测返回命中槽下标(= 原线性
|
||
# 扫描首个同名,首注册优先);未命中即"确无"精确键(键表只追加、键文本注册后
|
||
# 不改写,索引随扩容整体重建且覆盖全表)⇒ 跳过精确线性扫描(下方泛型特化归一化
|
||
# 匹配兜底仍保留,其键形态不同、不依赖精确键命中);索引不可信 → 回退线性扫描。
|
||
hit_r: int = -1
|
||
if _g_vpi_ret_idx_built == _g_vpi_ret_count \
|
||
and _g_vpi_ret_idx is not None and _g_vpi_ret_idx_cap > 0:
|
||
hit_r = _vpi_idx_find(_g_vpi_ret_keys, VPI_KEY_LEN,
|
||
_g_vpi_ret_idx, _g_vpi_ret_idx_cap, key_buf)
|
||
else:
|
||
# 回退原线性扫描:strcmp 判等蕴含首字节相等(键首字节 = src_sha1[0]),跳过首字节不同者。
|
||
key_first: int = key_buf[0]
|
||
for i in range(_g_vpi_ret_count):
|
||
ent_r: bytes = _g_vpi_ret_keys + i * VPI_KEY_LEN
|
||
if ent_r[0] == key_first and string.strcmp(ent_r, key_buf) == 0:
|
||
hit_r = i
|
||
break
|
||
if hit_r >= 0:
|
||
stdlib.free(key_buf)
|
||
return _g_vpi_ret_strs + hit_r * 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: sizet = sizet(uint64(lbkt_v) - uint64(cls_name))
|
||
param_len_v: sizet = sizet(uint64(rbkt_v) - uint64(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: sizet = sizet(
|
||
uint64(gklb) - uint64(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*,
|
||
seg: str) -> llvmlite.LLVMType*:
|
||
"""解析单个类型段为 LLVMType,无法识别返回 None"""
|
||
if seg is None or pool is None:
|
||
return None
|
||
# 【指针实参唯一规范形 `T*`(用户定口径,2026-09-30)】`rawptr[T]` ≡
|
||
# `t.CPtr[T]` ≡ `T | t.CPtr` ⇒ 一律折叠为 `T*`(见 TypeGenResolve
|
||
# ._norm_type_arg_name 规则 1.0/1.5)。为什么必须在此补:_subst_template_arg
|
||
# 会用**特化类名 `[...]` 内的已归一实参文本**(如 "{sha}.AST*")替换模板注解
|
||
# 里的 T,替换结果再送到本函数解析;本函数此前只认 `rawptr[` / `t.CPtr[` /
|
||
# 裸 `t.CPtr`,遇到 `{sha}.AST*` 会落到下方 strrchr('.') 取末段得 "AST*" →
|
||
# 查 struct miss → **静默**返回 None(跨模块参数/返回宽度被悄悄收窄)。
|
||
# 现场:Test/AstTest/App/main.vp:21 的 `list[ast.AST | t.CPtr] | t.CPtr` 特化链。
|
||
# 最小改法:把尾随 `*`(先跳尾随空格)按层数重写回**已存在**的 `rawptr[...]`
|
||
# 文本形再走原分支(与 `rawptr[` 分支逐字等价,不新写 Ptr 包装逻辑):
|
||
# "{sha}.AST*" → "rawptr[{sha}.AST]";"i8**" → "rawptr[rawptr[i8]]"。
|
||
seg_len_ps: sizet = string.strlen(seg)
|
||
star_end_ps: sizet = seg_len_ps
|
||
while star_end_ps > 0 and seg[star_end_ps - 1] == ' ':
|
||
star_end_ps -= 1
|
||
star_cnt_ps: sizet = 0
|
||
star_base_end_ps: sizet = star_end_ps
|
||
while star_base_end_ps > 0 and seg[star_base_end_ps - 1] == '*':
|
||
star_cnt_ps += 1
|
||
star_base_end_ps -= 1
|
||
if star_cnt_ps > 0 and star_base_end_ps > 0:
|
||
# 基数段长度 = star_base_end_ps;每层 "rawptr[" + "]" 共 8 字符
|
||
star_buf_len_ps: sizet = star_base_end_ps + 8 * star_cnt_ps + 1
|
||
star_buf_ps: bytes = stdlib.malloc(star_buf_len_ps)
|
||
if star_buf_ps is None:
|
||
return None
|
||
star_buf_ps[0] = '\0'
|
||
star_k_ps: sizet = 0
|
||
while star_k_ps < star_cnt_ps:
|
||
string.strcat(star_buf_ps, "rawptr[")
|
||
star_k_ps += 1
|
||
star_off_ps: sizet = string.strlen(star_buf_ps)
|
||
string.strncpy(star_buf_ps + star_off_ps, seg, star_base_end_ps)
|
||
star_buf_ps[star_off_ps + star_base_end_ps] = '\0'
|
||
star_k_ps = 0
|
||
while star_k_ps < star_cnt_ps:
|
||
string.strcat(star_buf_ps, "]")
|
||
star_k_ps += 1
|
||
star_ty_ps: llvmlite.LLVMType* = _resolve_single_vpi_type_str(pool, star_buf_ps)
|
||
stdlib.free(star_buf_ps)
|
||
return star_ty_ps
|
||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
||
i8_ptr_ty: llvmlite.LLVMType* = 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)
|
||
# rawptr[T] 内建裸指针(等价 t.CPtr[T] → Ptr(T)):vpi 返回/参数注解按源码
|
||
# 原样保留 Subscript 形态(如 tokens.vp `new_token -> rawptr[Token]`)。
|
||
# 此前无此分支 → 整个注解解析返回 None → text.ll 尚未生成(合法时序:依赖
|
||
# 模块尚未翻译,占位 text.ll 为空)时跨模块调用 fail-fast 误报
|
||
# (includes/ast/lexer.vp new_token 根因)。与定义侧 TypeGenResolve
|
||
# resolve_annotation_type 的 rawptr 分支同口径:Ptr(resolve(T))。
|
||
if string.strncmp(seg, "rawptr[", 7) == 0:
|
||
seg_len_rp: sizet = string.strlen(seg)
|
||
if seg_len_rp > 8 and seg[seg_len_rp - 1] == ']':
|
||
inner_len_rp: sizet = seg_len_rp - 8
|
||
inner_buf_rp: bytes = stdlib.malloc(inner_len_rp + 1)
|
||
if inner_buf_rp is not None:
|
||
string.strncpy(inner_buf_rp, seg + 7, inner_len_rp)
|
||
inner_buf_rp[inner_len_rp] = '\0'
|
||
inner_ty_rp: llvmlite.LLVMType* = _resolve_single_vpi_type_str(pool, inner_buf_rp)
|
||
stdlib.free(inner_buf_rp)
|
||
if inner_ty_rp is not None:
|
||
return llvmlite.Ptr(pool, inner_ty_rp)
|
||
return None
|
||
# t.CPtr[T] / c.CPtr[T] 下标指针(含嵌套,如 t.CPtr[t.CPtr] → Ptr(Ptr(i8))):与定义侧 TypeGenResolve 的 Subscript(CPtr) 分支同口径 Ptr(resolve(T))。
|
||
# vpi 注解文本按源码原样缓存;本函数若对该形态返回 None 会让该段被丢弃 → 联合坍缩到首段(i8),与定义侧指针层级不一致(跨模块 declare/define ABI 错位)。
|
||
cptr_sub_pfx: sizet = 0
|
||
if string.strncmp(seg, "t.CPtr[", 7) == 0 or string.strncmp(seg, "c.CPtr[", 7) == 0:
|
||
cptr_sub_pfx = 7
|
||
if cptr_sub_pfx > 0:
|
||
seg_len_cp: sizet = string.strlen(seg)
|
||
if seg_len_cp > cptr_sub_pfx + 1 and seg[seg_len_cp - 1] == ']':
|
||
inner_len_cp: sizet = seg_len_cp - cptr_sub_pfx - 1
|
||
inner_buf_cp: bytes = stdlib.malloc(inner_len_cp + 1)
|
||
if inner_buf_cp is not None:
|
||
string.strncpy(inner_buf_cp, seg + cptr_sub_pfx, inner_len_cp)
|
||
inner_buf_cp[inner_len_cp] = '\0'
|
||
inner_ty_cp: llvmlite.LLVMType* = _resolve_single_vpi_type_str(
|
||
pool, inner_buf_cp)
|
||
stdlib.free(inner_buf_cp)
|
||
if inner_ty_cp is not None:
|
||
return llvmlite.Ptr(pool, inner_ty_cp)
|
||
return None
|
||
# 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)
|
||
# [阶段2] 统一口径:CTypedef 动态表优先(库 typedef 走正常库分析),
|
||
# miss 回退 map_t_type(t/c 库内部特判)。
|
||
mapped_ty: llvmlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, attr_nm, None, None)
|
||
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* = 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* = 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* = GenApi.get_struct_type(seg)
|
||
if bare_struct_ty is not None:
|
||
return bare_struct_ty
|
||
# 裸枚举名 → EnumTable 兜底(同上:枚举不在 StructTable)
|
||
bare_enum_ty: llvmlite.LLVMType* = GenApi.get_enum_base_type(seg)
|
||
if bare_enum_ty is not None:
|
||
return bare_enum_ty
|
||
# 裸 typedef 名(如 VOIDPTR):CTypedef 动态表优先,miss 回退 map_t_type
|
||
mapped_bare: llvmlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, seg, None, None)
|
||
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 时节点 kind() 异常(可能退化为裸函数调用)导致 resolve 得到垃圾类型(如 Ptr(i8))。
|
||
#
|
||
# 支持: "str"/"bytes" → i8*; "int" → i32; "bool" → i1; "char" 等 → map_t_type 映射; "X | t.CPtr" → Ptr(Struct X); "t.CPtr" → i8*; "memhub.MemBuddy*" → Ptr(Struct MemBuddy)。
|
||
# ============================================================
|
||
def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*,
|
||
ret_annot: str,
|
||
i32_ty: llvmlite.LLVMType*,
|
||
i8_ptr_ty: llvmlite.LLVMType*) -> llvmlite.LLVMType*:
|
||
"""直接解析 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: sizet = 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
|
||
# tuple 返回类型的字段链表是 llvmlite.ParamNode(new_param_node / paramnode_set_next /
|
||
# Struct 形参均为 rawptr[ParamNode])——原先误注解为 Function*。
|
||
tup_head_t: llvmlite.ParamNode* = None
|
||
tup_tail_t: llvmlite.ParamNode* = None
|
||
tup_count_t: int = 0
|
||
p_t: sizet = 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: sizet = p_t
|
||
while p_t < inner_len_t and ret_annot[6 + p_t] != ',':
|
||
p_t += 1
|
||
seg_e_t: sizet = 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: sizet = 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* = _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* = 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* = None
|
||
# 产生 first_ty 那一段文本的属性(供末尾 has_cptr 分支复刻定义侧规则,N12):
|
||
# first_qual=1 段是限定名(含 '.',如 mod.Class / int64)
|
||
# first_wl =1 段是白名单裸标量名(int/char/str/bytes/bool/float/double)
|
||
# first_td =1 段是裸 typedef 别名且已成功解析出**非指针**类型(N13,
|
||
# 如 sizet/uint64 ⇒ i64):判据=“解析是否成功”,与定义侧
|
||
# TypeGenResolve.resolve_annotation_type 的 BinOp(A | t.CPtr)
|
||
# 分支同源(同一 resolve_typedef_or_map 口径)。
|
||
first_qual: int = 0
|
||
first_wl: int = 0
|
||
first_td: int = 0
|
||
# CPtr 下标段(t.CPtr[T]/c.CPtr[T],含嵌套)的联合合并参数:cptr_sub_lvl = 该段解析后的外层 Ptr 层数 N(如 t.CPtr[t.CPtr] ⇒ 2);cptr_sub_ty = 该段解析出的类型本身。
|
||
# 与定义侧 `X | t.CPtr[T]` 分支同口径:CPtr 侧贡献 N 层指针、另一侧提供基类型 ⇒ Ptr^N(基类型)。
|
||
# 与 has_cptr(裸 t.CPtr 段)分开记录:裸标记恒「追加 1 层」且有 struct 吸收特例,下标路径无吸收,两者在定义侧分属互斥分支。
|
||
cptr_sub_lvl: int = 0
|
||
cptr_sub_ty: llvmlite.LLVMType* = None
|
||
|
||
buf_len: sizet = string.strlen(ret_annot)
|
||
pos: sizet = 0
|
||
while pos <= buf_len:
|
||
# 跳过前导空格
|
||
while pos < buf_len and ret_annot[pos] == ' ':
|
||
pos += 1
|
||
seg_start: sizet = pos
|
||
# 只在**顶层**切 `|`:`[...]` 内的 `|` 属于泛型实参内部的联合注解,裸切会把首段
|
||
# 截成 `list[ast.AST`(来自 `list[ast.AST | t.CPtr] | t.CPtr`),该段再经
|
||
# strrchr('.') 落到 `AST` ⇒ 定义侧解析成 `AST*`(元素的指针),而正确答案是
|
||
# list 专化结构的指针(至少也应落 i8* 通配)。深度计数按 `[`/`]`,与
|
||
# _cache_vpi_param_annots_text 的顶层逗号切分同口径。
|
||
depth_ra: int = 0
|
||
while pos < buf_len:
|
||
ch_ra: char = ret_annot[pos]
|
||
if ch_ra == '[':
|
||
depth_ra += 1
|
||
elif ch_ra == ']':
|
||
if depth_ra > 0:
|
||
depth_ra -= 1
|
||
elif ch_ra == '|':
|
||
if depth_ra == 0:
|
||
break
|
||
pos += 1
|
||
seg_end: sizet = pos
|
||
# trim 尾随空格
|
||
while seg_end > seg_start and ret_annot[seg_end - 1] == ' ':
|
||
seg_end -= 1
|
||
if seg_end > seg_start:
|
||
seg_len2: sizet = 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'
|
||
# CPtr 下标段(t.CPtr[T]/c.CPtr[T],含嵌套如 t.CPtr[t.CPtr]):
|
||
# 必须先行识别,不能落普通段分支取 first_ty——定义侧
|
||
# TypeGenResolve.vp L2596-2639 对 `X | t.CPtr[T]` 是"CPtr 侧贡献
|
||
# N 层指针、另一侧给基类型 ⇒ Ptr^N(X)",与"把该段本身当联合取值"
|
||
# 语义不同(如 X=struct S、t.CPtr[t.CPtr]:定义侧 Ptr(Ptr(S)),
|
||
# 取该段本身则得 i8**)。此处只记录 N 与段类型,末尾统一合并。
|
||
if string.strncmp(seg_buf, "t.CPtr[", 7) == 0 or string.strncmp(seg_buf, "c.CPtr[", 7) == 0:
|
||
seg_ty_cs: llvmlite.LLVMType* = _resolve_single_vpi_type_str(pool, seg_buf)
|
||
if seg_ty_cs is not None:
|
||
# 计算外层 Ptr 嵌套数(定义侧 L2616-2632 同口径)。注意不能在
|
||
# case body 内更新 while 条件变量(自举编译的 match 会写到 subject
|
||
# 快照槽 → 条件读旧槽死循环,见 TypeGenResolve.vp L2620-2624):
|
||
# case 绑定 pointee 到局部变量,match 外更新循环变量。
|
||
lvl_cs: int = 0
|
||
cur_cs: llvmlite.LLVMType* = seg_ty_cs
|
||
while cur_cs is not None:
|
||
nxt_cs: llvmlite.LLVMType* = None
|
||
match cur_cs:
|
||
case llvmlite.LLVMType.Ptr(pointee_cs):
|
||
lvl_cs += 1
|
||
nxt_cs = pointee_cs
|
||
case _:
|
||
break
|
||
cur_cs = nxt_cs
|
||
# 多下标段取最大层数(定义侧按最外层联合右值合并,等价于层数累加)
|
||
if lvl_cs > cptr_sub_lvl:
|
||
cptr_sub_lvl = lvl_cs
|
||
cptr_sub_ty = seg_ty_cs
|
||
elif 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 \
|
||
or string.strcmp(seg_buf, "void") == 0:
|
||
# 裸名 void 是 t.CVoid 的别名(用户 2026-10-01):必须与 CVoid 同处理,
|
||
# 否则会走 else 分支取到 first_td=1 → Ptr(Void) 这种非法类型
|
||
# (void* 应为 i8*,由下方 has_cptr 分支统一给出)。
|
||
has_cvoid = 1
|
||
else:
|
||
seg_ty: llvmlite.LLVMType* = _resolve_single_vpi_type_str(pool, seg_buf)
|
||
if seg_ty is not None and first_ty is None:
|
||
first_ty = seg_ty
|
||
# 记录该段属性(N12):定义侧 TypeGenResolve.resolve_annotation_type
|
||
# 对**裸名** pointee 仅在它解析为已注册结构体、泛型参数名或白名单
|
||
# 裸标量时才保留 pointee,否则联合坍缩为 i8*;stub 侧此前无条件
|
||
# Ptr(first_ty) → `uint64 | t.CPtr` 两侧不一致(定义 i8* vs declare i64*)。
|
||
if string.strchr(seg_buf, 46) is not None:
|
||
first_qual = 1
|
||
if string.strcmp(seg_buf, "int") == 0 or \
|
||
string.strcmp(seg_buf, "char") == 0 or \
|
||
string.strcmp(seg_buf, "str") == 0 or \
|
||
string.strcmp(seg_buf, "bytes") == 0 or \
|
||
string.strcmp(seg_buf, "bool") == 0 or \
|
||
string.strcmp(seg_buf, "float") == 0 or \
|
||
string.strcmp(seg_buf, "double") == 0:
|
||
first_wl = 1
|
||
# 裸名 typedef 别名保真(N13,与定义侧同源):sizet/uint64/INT/u32 等别名若被判「非 struct 且不在白名单」会被坍缩成 i8*,
|
||
# 而定义侧已改为「解析成功即保留」⇒ 跨模块同型收敛被破坏。判据直接用 GenApi.resolve_typedef_or_map(与定义侧同一 API),命中即置位保留;
|
||
# 仅对**非指针**解析结果置位(指针别名 BYTEPTR/HANDLE/VOIDPTR 不置位 → 仍坍缩 i8*,与定义侧守卫对称)。
|
||
if first_qual == 0 and first_wl == 0:
|
||
if first_ty.kind() != llvmlite.LLVMType.Ptr:
|
||
if GenApi.resolve_typedef_or_map(pool, seg_buf, None, None) is not None:
|
||
first_td = 1
|
||
stdlib.free(seg_buf)
|
||
pos += 1 # 跳过 '|'
|
||
|
||
if has_cptr != 0:
|
||
if has_cvoid != 0:
|
||
return i8_ptr_ty
|
||
if first_ty is not None:
|
||
# 解析结果本身即 void(如 `typedef 不透明结构体 = void` 的名字经 typedef 兜底
|
||
# 解析出 void)⇒ 与 t.CPtr 合并即 void* = i8*,与 has_cvoid 分支同结论
|
||
# (否则会拼出非法的 Ptr(void))。
|
||
match first_ty:
|
||
case llvmlite.LLVMType.Void():
|
||
return i8_ptr_ty
|
||
case _:
|
||
pass
|
||
# 复刻定义侧规则(N12/N13):限定名 / 白名单裸标量 / 已注册结构体 / 可解析的非指针裸 typedef 别名 → 保留 pointee;
|
||
# 其余(解析失败、裸枚举名、指针别名)→ 联合坍缩为 i8*,与定义侧 resolve_annotation_type 对称(两侧同型即收敛)。
|
||
# 指针别名(first_td 不置位,如 VOIDPTR = CVoid|CPtr → i8*):`| t.CPtr`
|
||
# **恒追加恰好 1 层** → i8**(不吸收);指针型 T 同理(见下)。
|
||
if first_qual != 0 or first_wl != 0 or first_td != 0:
|
||
return llvmlite.Ptr(pool, first_ty)
|
||
if first_ty.kind() == llvmlite.LLVMType.Struct:
|
||
return llvmlite.Ptr(pool, first_ty)
|
||
if first_ty.kind() == llvmlite.LLVMType.Ptr:
|
||
# `| t.CPtr` **恒追加恰好 1 层**(与定义侧 TypeGenResolve 的 BinOp 分支同口径):
|
||
# 这里原先还做"pointee 命中 find_struct_by_type(类引用语义)就吸收 1 层",
|
||
# 那是"裸类名自动升指针"时代的补偿,自裸类名恒为**原型 struct** 后已无对象;
|
||
# 而泛型形参实例化为指针类型时(T 的实参文本 `{sha}.AST*`)first_ty 正是
|
||
# Ptr(已注册 struct) ⇒ 误吸收把 `T*` 降成 AST*(应为 AST**),
|
||
# 现场 includes/_list.vp:54 `elem_ptr[0] = item`。
|
||
return llvmlite.Ptr(pool, first_ty)
|
||
return i8_ptr_ty
|
||
return i8_ptr_ty
|
||
# CPtr 下标段联合合并(与定义侧同口径):有另一侧基类型 first_ty → 结果 = Ptr^N(first_ty)(N=cptr_sub_lvl);仅 CPtr 下标段自身 → 该段解析类型本身。
|
||
# 下标段无 struct 吸收,不与上方 has_cptr 分支的吸收判据混用;has_cptr 在上一分支已全部返回,二者不叠加。
|
||
if cptr_sub_lvl > 0:
|
||
if first_ty is None:
|
||
return cptr_sub_ty
|
||
res_ty_cs: llvmlite.LLVMType* = first_ty
|
||
wrap_i_cs: int = 0
|
||
while wrap_i_cs < cptr_sub_lvl:
|
||
res_ty_cs = llvmlite.Ptr(pool, res_ty_cs)
|
||
wrap_i_cs += 1
|
||
return res_ty_cs
|
||
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 语句);
|
||
# 本模块将其解析为 AST,提取 FunctionDef 与 CTypedef AnnAssign 并创建对应的 declare 声明。
|
||
# ============================================================
|
||
|
||
# .vpi 读取缓冲区大小(256KB,与 PYI_BUF_SIZE 一致)
|
||
VPI_READ_BUF_SIZE: sizet = 262144
|
||
|
||
# SHA1 hex 字符数(16,仅文件名中的十六进制位,不含结尾 NUL)
|
||
#
|
||
# 注意:本常量与 lib.Phase.Phase1 的 Phase1.SHA1_LEN(17 = 16 字符 + NUL 的存储步长)同名但语义不同;
|
||
# 同名跨模块 CDefine 会造成解析被遮蔽/串值(mb.alloc(0) 分配失败),故改为唯一名 SHA1_HEX_LEN。
|
||
SHA1_HEX_LEN: sizet = 16
|
||
|
||
|
||
# ============================================================
|
||
# _extract_sha1_from_path - 从 .vpi 文件路径提取 SHA1
|
||
#
|
||
# .vpi 文件名为 {sha1}.vpi(sha1 为 16 位十六进制字符):取最后一个路径分隔符(/ 或 \)之后文件名的前 16 个字符,分配新缓冲存储(调用者负责 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: sizet = string.strlen(vpi_path)
|
||
if total_len < SHA1_HEX_LEN + 4: # 至少需要 "xxxx.vpi" 的长度
|
||
return None
|
||
# 从后向前查找最后一个 / 或 \
|
||
sep_pos: sizet = total_len
|
||
while sep_pos > 0:
|
||
sep_pos -= 1
|
||
ch: char = vpi_path[sep_pos]
|
||
if ch == '/' or ch == '\\':
|
||
sep_pos += 1
|
||
break
|
||
if sep_pos + SHA1_HEX_LEN > total_len:
|
||
return None
|
||
# 分配缓冲区并复制 SHA1(16 字符 + null 终止符)
|
||
sha1_buf: str = stdlib.malloc(SHA1_HEX_LEN + 1)
|
||
if sha1_buf is None:
|
||
return None
|
||
string.strncpy(sha1_buf, vpi_path + sep_pos, SHA1_HEX_LEN)
|
||
sha1_buf[SHA1_HEX_LEN] = '\0'
|
||
return sha1_buf
|
||
|
||
|
||
# ============================================================
|
||
# stub 导入上下文记账:一律走 StubMergerStore(单一真源)
|
||
#
|
||
# 原 _stub_add_imported / _stub_add_from 是本模块内的两份私有副本,与翻译期
|
||
# ImportsGenMixin.add_imported_module / add_from_import 重复;其中 Import 分支
|
||
# 只抄了 imported_modules 的追加,漏掉 from_imports 的别名映射,导致 vpi 注解
|
||
# 前向声明解析不到模块 SHA1(GenTypes.VarEntry.Spec.PtrLevel 失败根因)。
|
||
# 记账已统一到 StubMergerStore.AppendImportedModule / AppendFromImport /
|
||
# NotePlainImport(纯记账、无索引副作用,正合 stub 上下文所需)。
|
||
# ============================================================
|
||
|
||
|
||
# ============================================================
|
||
# 递归 typedef 预注册(import 闭包)
|
||
#
|
||
# CTypedef 表是每文件隔离的:当前模块若未直接 import typedef 定义所在模块(如 UINT 在 stdint.vp),其 stub 未注入 → 表里没有该 typedef
|
||
# → 参数类型解析失败退化为 i8_ptr_ty → 调用侧整数实参触发「整数隐式转指针已禁止」CastError 误报。
|
||
# 本函数在被读 stub 的 import 闭包上递归注册 CTypedef/CDefine(防环、限深),使注解解析上下文完整,参数/返回类型不再静默退化。
|
||
# ============================================================
|
||
_TYPEDEF_VISIT_MAX: t.CDefine = 1024
|
||
|
||
_g_vpi_typedef_visited: bytes = None
|
||
_g_vpi_typedef_visited_count: int = 0
|
||
# 访问表当前容量(元素数)。**表满必须扩容,不允许静默丢标记**:
|
||
# 旧实现 `count >= _TYPEDEF_VISIT_MAX 就 return`(丢标记)⇒ 防环失效 ⇒ 递归闭包
|
||
# 反复重解析同一批 .vpi(限深 8 也挡不住宽度爆炸),每个 vpi 的 Lexer/Token/AST 都
|
||
# 从池里分配且本窗口不回收 ⇒ 单次 translate 吃掉数百 MB ⇒ 1 GiB 池 OOM
|
||
# (OOM 报告特征:ctx=translate/<json 模块>、chk_depth 数万、free_bytes=0、
|
||
# 「镜像存活=0 块」且记录项块头读作 0x0)。
|
||
_g_vpi_typedef_visited_cap: int = 0
|
||
|
||
|
||
def ResetVpiTypedefVisited() -> None:
|
||
"""清空递归 typedef 注册的防环访问标记(每个模块的 stub 注入前调用)"""
|
||
global _g_vpi_typedef_visited, _g_vpi_typedef_visited_count, _g_vpi_typedef_visited_cap
|
||
if _g_vpi_typedef_visited is not None:
|
||
stdlib.free(_g_vpi_typedef_visited)
|
||
_g_vpi_typedef_visited = None
|
||
_g_vpi_typedef_visited_count = 0
|
||
_g_vpi_typedef_visited_cap = 0
|
||
|
||
|
||
def _vpi_typedef_visited_check(sha1: str) -> int:
|
||
"""检查 sha1 是否已递归注册过(1=已处理,0=未处理;纯查询不标记)"""
|
||
global _g_vpi_typedef_visited, _g_vpi_typedef_visited_count
|
||
if sha1 is None:
|
||
return 1
|
||
if _g_vpi_typedef_visited is None:
|
||
return 0
|
||
vi: int
|
||
for vi in range(_g_vpi_typedef_visited_count):
|
||
if string.strcmp(_g_vpi_typedef_visited + vi * 17, sha1) == 0:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _vpi_typedef_visited_mark(sha1: str) -> None:
|
||
"""标记 sha1 为已递归注册(成功后调用,防环)"""
|
||
global _g_vpi_typedef_visited, _g_vpi_typedef_visited_count, _g_vpi_typedef_visited_cap
|
||
if sha1 is None:
|
||
return
|
||
if _g_vpi_typedef_visited is None:
|
||
_g_vpi_typedef_visited = stdlib.malloc(_TYPEDEF_VISIT_MAX * 17)
|
||
if _g_vpi_typedef_visited is None:
|
||
# fail-fast:没有防环表 ⇒ 递归闭包重解析爆炸(池 OOM),不能静默继续
|
||
stdio.printf("[VPI] 防环访问表分配失败(无表则闭包重解析爆炸,终止)\n")
|
||
stdio.fflush(None)
|
||
stdlib.exit(1)
|
||
string.memset(_g_vpi_typedef_visited, 0, _TYPEDEF_VISIT_MAX * 17)
|
||
_g_vpi_typedef_visited_count = 0
|
||
_g_vpi_typedef_visited_cap = _TYPEDEF_VISIT_MAX
|
||
if _g_vpi_typedef_visited_count >= _g_vpi_typedef_visited_cap:
|
||
# 表满:扩容(×2),**绝不能静默丢标记**——丢标记 = 防环失效 = 同一批 .vpi 被
|
||
# 反复重解析(每个 vpi 的 Lexer/Token/AST 都从池分配且本窗口不回收)⇒ 池 OOM。
|
||
old_cap: int = _g_vpi_typedef_visited_cap
|
||
new_cap: int = old_cap * 2
|
||
nb: bytes = stdlib.realloc(_g_vpi_typedef_visited, new_cap * 17)
|
||
if nb is None:
|
||
stdio.printf("[VPI] 防环访问表扩容失败 cap=%d(丢标记引发重解析爆炸,终止)\n",
|
||
old_cap)
|
||
stdio.fflush(None)
|
||
stdlib.exit(1)
|
||
_g_vpi_typedef_visited = nb
|
||
string.memset(nb + old_cap * 17, 0, old_cap * 17)
|
||
_g_vpi_typedef_visited_cap = new_cap
|
||
string.strcpy(_g_vpi_typedef_visited + _g_vpi_typedef_visited_count * 17, sha1)
|
||
_g_vpi_typedef_visited_count += 1
|
||
|
||
|
||
def _vpi_register_typedef_closure(pool: memhub.MemBuddy*,
|
||
import_mods: str,
|
||
temp_dir: str,
|
||
depth: int) -> int:
|
||
"""递归注册 import 闭包模块的 CTypedef/CDefine,返回 0 恒(副作用注册)
|
||
|
||
仅注册 AnnAssign 的 t.CTypedef/t.CDefine,不注入函数 declare(避免
|
||
传递依赖的函数表膨胀)。vpi 文件缺失(Phase A 早期 stub 未生成)时
|
||
静默跳过,Phase 2 stub 就绪后自然命中。
|
||
"""
|
||
if pool is None or import_mods is None or temp_dir is None:
|
||
return 0
|
||
if depth > 8:
|
||
return 0
|
||
td_len: sizet = string.strlen(temp_dir)
|
||
total_len: sizet = string.strlen(import_mods)
|
||
pos: sizet = 0
|
||
while pos < total_len:
|
||
while pos < total_len and import_mods[pos] == ' ':
|
||
pos += 1
|
||
if pos >= total_len:
|
||
break
|
||
m_start: sizet = pos
|
||
while pos < total_len and import_mods[pos] != ' ':
|
||
pos += 1
|
||
m_len: sizet = pos - m_start
|
||
if m_len == 0:
|
||
continue
|
||
# t/c 内建库不编译,无 vpi stub
|
||
if m_len == 1 and (import_mods[m_start] == 't' or import_mods[m_start] == 'c'):
|
||
continue
|
||
mod_buf: bytes = stdlib.malloc(m_len + 1)
|
||
if mod_buf is None:
|
||
continue
|
||
string.strncpy(mod_buf, import_mods + m_start, m_len)
|
||
mod_buf[m_len] = '\0'
|
||
sha1_r: str = StubMergerStore.LookupSha1ByModName(mod_buf)
|
||
stdlib.free(mod_buf)
|
||
if sha1_r is None:
|
||
continue
|
||
if _vpi_typedef_visited_check(sha1_r) != 0:
|
||
continue
|
||
# 构造 {temp_dir}/{sha1[0:2]}/{sha1}.vpi(手写拼接,与
|
||
# _ensure_vpi_ret_annot_loaded 一致,避免引入 StubMergerCollect 环依赖)
|
||
path_len_r: sizet = td_len + 16 + 12
|
||
vpi_path_r: bytes = stdlib.malloc(path_len_r)
|
||
if vpi_path_r is None:
|
||
continue
|
||
string.strcpy(vpi_path_r, temp_dir)
|
||
vpi_path_r[td_len] = '/'
|
||
vpi_path_r[td_len + 1] = sha1_r[0]
|
||
vpi_path_r[td_len + 2] = sha1_r[1]
|
||
vpi_path_r[td_len + 3] = '/'
|
||
string.strcpy(vpi_path_r + td_len + 4, sha1_r)
|
||
string.strcpy(vpi_path_r + td_len + 4 + 16, ".vpi")
|
||
f_r: rawptr[fileio.File] = fileio.File(vpi_path_r, fileio.MODE.R)
|
||
if f_r.closed:
|
||
stdlib.free(vpi_path_r)
|
||
continue
|
||
vpi_buf_r: bytes = stdlib.malloc(VPI_READ_BUF_SIZE)
|
||
if vpi_buf_r is None:
|
||
f_r.close()
|
||
stdlib.free(vpi_path_r)
|
||
continue
|
||
bytes_read_r: LONG = f_r.read_all(vpi_buf_r, VPI_READ_BUF_SIZE)
|
||
f_r.close()
|
||
if bytes_read_r > 0:
|
||
# 成功读到 vpi 才标记已处理(文件缺失/读取失败不标记,
|
||
# 后续 pass 的 stub 注入可重试)
|
||
_vpi_typedef_visited_mark(sha1_r)
|
||
if bytes_read_r < VPI_READ_BUF_SIZE:
|
||
vpi_buf_r[bytes_read_r] = 0
|
||
lx_r: ast.Lexer* = ast.new_lexer(pool)
|
||
if lx_r is not None:
|
||
ast._lexer_init(lx_r, vpi_buf_r, pool)
|
||
tokens_r: ast.Token* = ast.tokenize(lx_r)
|
||
tree_r: ast.AST* = ast.parse_tokens(pool, tokens_r)
|
||
if tree_r is not None and tree_r.children is not None:
|
||
ch_r: list[ast.AST*]* = tree_r.children
|
||
cn_r: sizet = len(ch_r)
|
||
# 收集子模块 imports(递归闭包):两遍处理——先累计所需长度,再按精确长度分配后填充,不存在长度上限,故无需丢弃。
|
||
# (定长缓冲 + "装不下就静默丢弃" 会让闭包缺模块 ⇒ 该模块的 CTypedef/CDefine 不可见 ⇒ 注解解析退化。)
|
||
# 计数阶段每项多算 1 字节分隔符(首项不用),偏保守,无害。
|
||
sub_need: sizet = 1 # 末尾 NUL
|
||
ci_c: sizet
|
||
for ci_c in range(cn_r):
|
||
child_c: ast.AST* = ch_r.get(ci_c)
|
||
if child_c is None:
|
||
continue
|
||
kk_c: int = child_c.kind()
|
||
if kk_c == ast.ASTKind.Import:
|
||
imp_c: ast.Import* = ast.Import*(child_c)
|
||
if imp_c.names is not None:
|
||
in_nc: sizet = len(imp_c.names)
|
||
for ii_c in range(in_nc):
|
||
al_c: ast.Alias* = ast.Alias*(imp_c.names.get(ii_c))
|
||
if al_c is not None and al_c.name is not None:
|
||
sub_need += string.strlen(al_c.name) + 1
|
||
elif kk_c == ast.ASTKind.ImportFrom:
|
||
ifr_c: ast.ImportFrom* = ast.ImportFrom*(child_c)
|
||
if ifr_c.module is not None:
|
||
sub_need += string.strlen(ifr_c.module) + 1
|
||
sub_imports: bytes = stdlib.malloc(sub_need)
|
||
if sub_imports is not None:
|
||
sub_imports[0] = '\0'
|
||
sub_len: sizet = 0
|
||
ci_r: sizet
|
||
for ci_r in range(cn_r):
|
||
child_r: ast.AST* = ch_r.get(ci_r)
|
||
if child_r is None:
|
||
continue
|
||
kk_r: int = child_r.kind()
|
||
if kk_r == ast.ASTKind.Import:
|
||
imp_r: ast.Import* = ast.Import*(child_r)
|
||
if imp_r.names is not None:
|
||
in_n: sizet = len(imp_r.names)
|
||
ii_r: sizet
|
||
for ii_r in range(in_n):
|
||
al_r: ast.Alias* = ast.Alias*(imp_r.names.get(ii_r))
|
||
if al_r is not None and al_r.name is not None:
|
||
il_r: sizet = string.strlen(al_r.name)
|
||
if sub_len > 0:
|
||
sub_imports[sub_len] = ' '
|
||
sub_len += 1
|
||
string.strcpy(sub_imports + sub_len, al_r.name)
|
||
sub_len += il_r
|
||
sub_imports[sub_len] = '\0'
|
||
elif kk_r == ast.ASTKind.ImportFrom:
|
||
ifr_r: ast.ImportFrom* = ast.ImportFrom*(child_r)
|
||
if ifr_r.module is not None:
|
||
ml_r: sizet = string.strlen(ifr_r.module)
|
||
if sub_len > 0:
|
||
sub_imports[sub_len] = ' '
|
||
sub_len += 1
|
||
string.strcpy(sub_imports + sub_len, ifr_r.module)
|
||
sub_len += ml_r
|
||
sub_imports[sub_len] = '\0'
|
||
# 第二遍:注册本模块 AnnAssign 的 CTypedef/CDefine
|
||
ci2_r: sizet
|
||
for ci2_r in range(cn_r):
|
||
child2_r: ast.AST* = ch_r.get(ci2_r)
|
||
if child2_r is None:
|
||
continue
|
||
if child2_r.kind() != ast.ASTKind.AnnAssign:
|
||
continue
|
||
aa_r: ast.AnnAssign* = ast.AnnAssign*(child2_r)
|
||
if aa_r.annotation is None or aa_r.target is None or aa_r.value is None:
|
||
continue
|
||
if GenApi._is_ctypedef_annotation(aa_r.annotation) != 0:
|
||
_register_ctypedef_from_annassign(pool, aa_r)
|
||
elif GenApi.is_cdefine_annotation(aa_r.annotation) != 0:
|
||
if aa_r.target.kind() != ast.ASTKind.Name:
|
||
continue
|
||
cdef_t: ast.Name* = ast.Name*(aa_r.target)
|
||
if cdef_t.id is None:
|
||
continue
|
||
if aa_r.value.kind() == ast.ASTKind.Constant:
|
||
cn_v: ast.Constant* = ast.Constant*(aa_r.value)
|
||
if cn_v.const_kind == ast.CONST_FLOAT:
|
||
GenApi.register_cdefine_double(pool, cdef_t.id, cn_v.float_val)
|
||
elif cn_v.const_kind == ast.CONST_INT:
|
||
cdef_val: int64 = GenApi.extract_cdefine_int_value(aa_r.value)
|
||
GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val)
|
||
# 递归处理子模块 imports(防环由 visited 标记保证)
|
||
if sub_imports[0] != '\0':
|
||
_vpi_register_typedef_closure(pool, sub_imports, temp_dir, depth + 1)
|
||
stdlib.free(sub_imports)
|
||
stdlib.free(vpi_buf_r)
|
||
stdlib.free(vpi_path_r)
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# ParseVpiIntoModule - 解析 .vpi 文件,将函数声明注入到当前模块
|
||
#
|
||
# 读取 .vpi → AST 解析 → 遍历顶层节点创建 declare:对每个 FunctionDef 提取函数名/参数/返回类型,检测 CExport/CExtern/State 标记(决定是否用裸名),
|
||
# 用 find_func_in_module 去重,再用 create_declare + add_param 创建声明并以裸名(非 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*,
|
||
mod: llvmlite.LLVMModule*,
|
||
vpi_path: str,
|
||
trans: HT.Translator*) -> 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*,
|
||
mod: llvmlite.LLVMModule*,
|
||
vpi_path: str,
|
||
trans: HT.Translator*) -> 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: rawptr[fileio.File] = 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
|
||
|
||
# 2. 解析为 AST
|
||
lx: ast.Lexer* = ast.new_lexer(pool)
|
||
if lx is None:
|
||
stdlib.free(vpi_buf)
|
||
return 0
|
||
ast._lexer_init(lx, vpi_buf, pool)
|
||
tokens: ast.Token* = ast.tokenize(lx)
|
||
tree: ast.AST* = 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*]* = tree.children
|
||
if ch is None:
|
||
if src_sha1 is not None:
|
||
stdlib.free(src_sha1)
|
||
return 1
|
||
n: sizet = 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
|
||
# 记账统一走 StubMergerStore(单一真源,与翻译期 ImportsGenMixin.translate_import
|
||
# 同口径):含 `import X as Y → from_imports "Y:X"`(别名反查模块真名)与
|
||
# `import a.b.c → imported_modules "a"`(顶层包名)。
|
||
stub_ctx: StubMergerStore.ImportCtx* = StubMergerStore.AllocImportCtx(pool, None, None)
|
||
for si in range(n):
|
||
schild: ast.AST* = ch.get(si)
|
||
if schild is None:
|
||
continue
|
||
sk: int = schild.kind()
|
||
if sk == ast.ASTKind.Import:
|
||
simp: ast.Import* = ast.Import*(schild)
|
||
if simp.names is not None:
|
||
sn_n: sizet = len(simp.names)
|
||
for sni in range(sn_n):
|
||
sal: ast.Alias* = ast.Alias*(simp.names.get(sni))
|
||
if sal is not None and sal.name is not None:
|
||
StubMergerStore.NotePlainImport(pool, stub_ctx, sal.name, sal.asname)
|
||
elif sk == ast.ASTKind.ImportFrom:
|
||
sifr: ast.ImportFrom* = ast.ImportFrom*(schild)
|
||
sifr_mod: str = sifr.module
|
||
# 相对导入补包前缀 + 包 __init__ 判定(两者**都不再依赖 rel_path 表**)。
|
||
#
|
||
# vpi 里 `from .base import X` 只存 module="base"(前导点记在 level),按字面
|
||
# "base" 精确查找查不到 "ast.base" ⇒ 裸类名(AST 等)解析失败。包前缀的
|
||
# 权威来源是**模块名表**:兄弟模块 S 的全名 = "<包前缀>.S"(如 "ast.stmts")
|
||
# ⇒ 包前缀 = S 的模块名去掉最后一段;而 Utils.PathToModuleName 把
|
||
# `pkg/__init__.vp` 记成 "pkg"(**不带** .__init__,见其 :150 文档)
|
||
# ⇒ "本 stub 的模块名 == 该包前缀"即证明**本 stub 就是该包的 __init__**,
|
||
# 这正是重导出别名 entry 的登记条件。
|
||
# 为什么要脱离 rel_path:rel_path 反查在部分运行形态下取不到条目 ⇒
|
||
# 原 `strstr(rel_path,"__init__.vp")` 判定恒 false ⇒ 别名静默不登记 ⇒
|
||
# `ast.ClassDef` 这类**限定名在泛型实参里保留包前缀**,与构造函数侧(真
|
||
# 定义模块前缀)分裂成两个类型(现场 ClassGenGeneric.vp:92 的 CastError;
|
||
# 幽灵名 `list[1abcd5277fa442de.ClassDef*]` 在全部产物里恒为 opaque)。
|
||
# 取不到模块名证据时回退旧的 rel_path 路线,行为不变。
|
||
pkg_prefix_rt: str = None
|
||
if sifr.level > 0 and sifr_mod is not None and sifr_mod[0] != '\0':
|
||
_sib_sha_rt: str = StubMergerStore.LookupSha1ByModNameSuffix(sifr_mod)
|
||
if _sib_sha_rt is not None:
|
||
_sub_mod_rt: str = StubMergerStore.LookupModNameBySha1(_sib_sha_rt)
|
||
if _sub_mod_rt is not None:
|
||
_dot_rt: str = string.strrchr(_sub_mod_rt, '.')
|
||
if _dot_rt is not None:
|
||
_pfx_len_rt: sizet = sizet(uint64(_dot_rt) - uint64(_sub_mod_rt))
|
||
if _pfx_len_rt > 0:
|
||
_my_mod_rt: str = StubMergerStore.LookupModNameBySha1(src_sha1)
|
||
# 确认:本 stub 的模块名必须**恰好等于**该前缀(= 本 stub
|
||
# 就是包 __init__)。后缀命中可能落到同后缀的别的模块,
|
||
# 故必须用本 stub 自己的模块名确认,不确认就让位给 rel_path。
|
||
_conf_rt: int = 0
|
||
if _my_mod_rt is not None and string.strlen(_my_mod_rt) == _pfx_len_rt:
|
||
if string.strncmp(_my_mod_rt, _sub_mod_rt, _pfx_len_rt) == 0:
|
||
_conf_rt = 1
|
||
if _conf_rt != 0:
|
||
pkg_prefix_rt = pool.alloc(_pfx_len_rt + 1)
|
||
if pkg_prefix_rt is not None:
|
||
string.strncpy(pkg_prefix_rt, _sub_mod_rt, _pfx_len_rt)
|
||
pkg_prefix_rt[_pfx_len_rt] = '\0'
|
||
sifr_mod_q: str = sifr_mod
|
||
if sifr_mod is not None and sifr_mod[0] != '\0' and sifr.level == 1:
|
||
if pkg_prefix_rt is not None:
|
||
qtotal_p: sizet = string.strlen(pkg_prefix_rt) + 1 + string.strlen(sifr_mod) + 1
|
||
qbuf_p: str = pool.alloc(qtotal_p)
|
||
if qbuf_p is not None:
|
||
qbuf_p[0] = '\0'
|
||
string.strcat(qbuf_p, pkg_prefix_rt)
|
||
string.strcat(qbuf_p, ".")
|
||
string.strcat(qbuf_p, sifr_mod)
|
||
sifr_mod_q = qbuf_p
|
||
else:
|
||
rel_p_r: str = StubMergerStore.LookupSha1RelPath(src_sha1)
|
||
if rel_p_r is not None:
|
||
slash_r: str = string.strrchr(rel_p_r, '/')
|
||
if slash_r is not None:
|
||
pkg_len_r: sizet = string.strlen(rel_p_r) - string.strlen(slash_r)
|
||
if pkg_len_r > 0:
|
||
qtotal_r: sizet = pkg_len_r + 1 + string.strlen(sifr_mod) + 1
|
||
qbuf_r: str = pool.alloc(qtotal_r)
|
||
if qbuf_r is not None:
|
||
string.strncpy(qbuf_r, rel_p_r, pkg_len_r)
|
||
qbuf_r[pkg_len_r] = '.'
|
||
string.strcpy(qbuf_r + pkg_len_r + 1, sifr_mod)
|
||
sifr_mod_q = qbuf_r
|
||
if sifr_mod is not None:
|
||
if stub_ctx is not None:
|
||
stub_ctx.ImportedModules = StubMergerStore.AppendImportedModule(
|
||
pool, stub_ctx.ImportedModules, sifr_mod_q)
|
||
# 方案A:包 __init__.vpi 的重导出类以包 sha1 注册别名 entry
|
||
# (ast.Attribute 定义在 ast.exprs 但归属 ast 包;查找端按包
|
||
# sha1 匹配,注册端按定义文件 sha1 → name+sha1 错位 fail-fast。
|
||
# 仅 __init__ 触发,普通模块 vpi 的跨模块 from-import 不注册,
|
||
# 避免把类错误归属到当前模块 sha1)
|
||
# 包 __init__ 判定(**首选不依赖 rel_path 表**):pkg_prefix_rt 非空
|
||
# 即上面的模块名证据成立——"本 stub 的模块名 == 兄弟模块名去掉末段"
|
||
# ⇒ 本 stub 就是该包的 __init__(同一事实,不重复计算)。
|
||
# rel_path 反查取不到条目时原判定恒 false ⇒ 重导出别名静默不登记 ⇒
|
||
# `ast.ClassDef` 这类限定名在泛型实参里保留**包前缀**,与定义模块
|
||
# 前缀分裂(现场 ClassGenGeneric.vp:92 的 CastError)。
|
||
# 仅当模块名证据不足时才回退旧 rel_path 判定(行为不变)。
|
||
is_pkg_init_rt: int = 0
|
||
if pkg_prefix_rt is not None:
|
||
is_pkg_init_rt = 1
|
||
if is_pkg_init_rt == 0:
|
||
rel_path_rt: str = StubMergerStore.LookupSha1RelPath(src_sha1)
|
||
if rel_path_rt is not None:
|
||
# [FIX] 原判定 strcmp(rel_path + strlen(rel_path) - 9, "__init__.vp")
|
||
# 假设 "__init__.vp" 从 len-9 开始;实际包路径 "ast/__init__.vp"
|
||
# 长 15,该后缀从索引 4 起(11 字符),len-9=6 指向 "init__.vp"
|
||
# → 判定恒 false → 包重导出永不触发(ast.Attribute 未注册根因之一)。
|
||
# 改用 strstr 判定 rel_path 包含 "__init__.vp"(仅包 __init__ 文件命中)。
|
||
if string.strstr(rel_path_rt, "__init__.vp") is not None:
|
||
is_pkg_init_rt = 1
|
||
# 解析被 from-import 的模块 sha1:相对导入按 parser 表示判定——
|
||
# 前导点存于 level(from .exprs import → level=1, module="exprs",
|
||
# 见 includes/ast/stmts.vp ImportFrom.level / parser.vp _parse_from_import),
|
||
# module 字段不含前导点。原判定 sifr_mod[0]=='.' 永远不成立 → 走绝对
|
||
# 分支 LookupSha1ByModName("exprs") 查不到 "ast.exprs" → sub_sha1=None
|
||
# → 重导出被跳过(ast.Attribute 未注册根因之二)。
|
||
sub_sha1_rt: str = None
|
||
if sifr.level > 0:
|
||
if sifr_mod is not None and sifr_mod[0] != '\0':
|
||
# 优先用补前缀后的**精确**模块名(sifr_mod_q,如 ast.base)——
|
||
# 后缀匹配只作旧行为兜底:后缀命中可能落到别的同后缀模块,
|
||
# 而下面的按需注入会按该 sha1 拉 vpi,注入错模块代价大。
|
||
sub_sha1_rt = StubMergerStore.LookupSha1ByModName(sifr_mod_q)
|
||
if sub_sha1_rt is None:
|
||
sub_sha1_rt = StubMergerStore.LookupSha1ByModNameSuffix(sifr_mod)
|
||
else:
|
||
sub_sha1_rt = StubMergerStore.LookupSha1ByModName(sifr_mod)
|
||
# [D26 fail-fast] 包 __init__ 的 from-import 解析不出子模块 sha1 时
|
||
# **不再静默跳过**:跳过会让该子模块的类一个都注册不上 → 包 stub 里
|
||
# 引用这些类的注解在后续解析时落空 → 报错点远离真因(实测:报错指向
|
||
# ast/__init__.vpi 的 `parse_expression -> rawptr[Expression]`,而
|
||
# Expression 定义在 ast.stmts,真因是子模块 sha1 查找/注入失败)。
|
||
# 此处直接给出包名与子模块名(含补前缀后的精确名)便于定位。
|
||
if is_pkg_init_rt != 0 and sub_sha1_rt is None \
|
||
and sifr_mod is not None and sifr_mod[0] != '\0':
|
||
GenApi.fatal_error(
|
||
sifr,
|
||
f"包 stub 的 from-import 子模块 sha1 查找失败:"
|
||
f"包={rel_path_rt if rel_path_rt is not None else '?'},"
|
||
f"子模块='{sifr_mod}'(补前缀后='{sifr_mod_q}');"
|
||
f"该子模块的类无法注册,引用它们的注解会在后续解析时落空",
|
||
"NameError")
|
||
if sifr.names is not None:
|
||
sifr_nn: sizet = len(sifr.names)
|
||
sifri: sizet
|
||
for sifri in range(sifr_nn):
|
||
sial: ast.Alias* = ast.Alias*(sifr.names.get(sifri))
|
||
if sial is not None and sial.name is not None:
|
||
slocal: str = sial.name
|
||
sorig: str = None
|
||
if sial.asname is not None:
|
||
slocal = sial.asname
|
||
sorig = sial.name
|
||
# `from X import *` 亦写 "*:mod"(与翻译期
|
||
# ImportsGenMixin.translate_from_import_names 同口径):
|
||
# lookup_from_import 的 star 回退正是靠这条 entry,旧实现
|
||
# 在此跳过 ⇒ stub 上下文里 star 回退恒失效(同一份约定
|
||
# 的两处 loop 又一处不一致)。
|
||
if stub_ctx is not None:
|
||
stub_ctx.FromImports = StubMergerStore.AppendFromImport(
|
||
pool, stub_ctx.FromImports, slocal, sifr_mod_q, sorig)
|
||
# 重导出类以包 sha1 注册(asname 时按别名注册);star 无
|
||
# 具体类名,不参与重导出(仅记账,见上)。
|
||
if string.strcmp(sial.name, "*") != 0:
|
||
if is_pkg_init_rt != 0 and sub_sha1_rt is not None:
|
||
_inject_reexport_class_declare(
|
||
pool, mod, sial.name, slocal,
|
||
sub_sha1_rt, src_sha1, trans)
|
||
|
||
# 用 stub 自己的 imports 替代当前模块的(stub 注解解析上下文正确性)
|
||
if stub_ctx is not None:
|
||
stub_imported_modules = stub_ctx.ImportedModules
|
||
stub_from_imports = stub_ctx.FromImports
|
||
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[uint8, 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: sizet = 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: sizet
|
||
for sri_src in range(sr_len_src):
|
||
ch_src: char = 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 = StubMergerStore.AppendImportedModule(
|
||
pool, imported_modules, src_mod_buf)
|
||
stdlib.free(src_mod_buf)
|
||
|
||
# 递归预注册 stub import 闭包的 CTypedef/CDefine:注解(如 codepage:
|
||
# UINT)的 typedef 定义在 stdint.vp,但 CTypedef 表每文件隔离——当前
|
||
# 模块未直接 import stdint 时表里没有 UINT → 参数类型退化为 i8_ptr_ty
|
||
# → 调用侧整数实参误报 CastError(testcheck.SetConsoleOutputCP 根因)。
|
||
if imported_modules is not None:
|
||
_vpi_register_typedef_closure(pool, imported_modules, GenApi.get_temp_dir(), 0)
|
||
|
||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
||
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
|
||
|
||
# 函数表信息
|
||
funcs_ptr: GenTypes.FuncEntry* = trans._funcs
|
||
max_funcs: int = 1024
|
||
|
||
# ============================================================
|
||
# REPL(ReplStubLayoutMode):先从 .stub.ll 恢复各类的**完整布局**
|
||
# (__vtable__ + 父类展平字段 + 本类字段)与虚表方法索引,再走下方既有
|
||
# vpi 注入(已登记者只标可见)。详见文件末"REPL:includes 类完整布局与
|
||
# 虚表恢复"段;批处理该标志恒 0,本块不执行。
|
||
# ============================================================
|
||
if ReplStubLayoutMode != 0:
|
||
_repl_stub_collect_module(pool, mod, ch, n, trans, src_sha1,
|
||
stub_imported_modules, stub_from_imports)
|
||
|
||
# ============================================================
|
||
# 第一遍 A:先注册**所有** ClassDef(字段 + 结构体),不处理方法。
|
||
# 否则类定义位于全局变量之后时(如 devfs.vp 的 `directories: t.CArray[devfs_directory, MAX]` 在 `class devfs_directory` 之前),
|
||
# AnnAssign 解析裸类名时类未注册 → CArray 元素解析失败 fatal。
|
||
# ============================================================
|
||
i0: sizet
|
||
for i0 in range(n):
|
||
child0: ast.AST* = ch.get(i0)
|
||
if child0 is None:
|
||
continue
|
||
if child0.kind() != ast.ASTKind.ClassDef:
|
||
continue
|
||
class_cd0: ast.ClassDef* = ast.ClassDef*(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]* = 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)
|
||
|
||
# App 预注册(Phase A-pre,_pre_allow_dep_inject)**跳过方法/函数 declare 注入**:
|
||
# 预注册的唯一目的是注册 struct/enum/union(第一遍 A)与包重导出别名(上方
|
||
# ImportFrom 段),方法/函数 declare 写在其瞬时模块里、随即随 mb.reset_to 丢弃,
|
||
# 不产生任何跨遍作用。而依赖 vpi 的形参/返回注解可能引用**本遍尚未注册**的类
|
||
# (跨文件注册顺序:被依赖模块的类可能排在更后,需靠下一遍收敛);此时
|
||
# resolve 落空会在 declare 注入里 fail-fast,直接打断收敛。
|
||
# 现场:App 预注册 AssignGenMixin.vp 注入 Translator.vpi 时,其
|
||
# InjectTracebackInstallCtor 形参 rawptr[Translator] 解析失败(Translator
|
||
# 结构体因字段引用尚未注册的 GenTypes 类而本遍未登记)。
|
||
# 全量翻译(_declare_only==0,_pre_allow_dep_inject==0)不跳过,仍 fail-fast。
|
||
pre_skip_fn_declares: int = 0
|
||
if trans is not None and trans._pre_allow_dep_inject != 0:
|
||
pre_skip_fn_declares = 1
|
||
|
||
# ============================================================
|
||
# 第一遍 B:**全部类注册完成之后**再处理方法 declare——方法形参/返回值可能引用本文件更靠后定义的类(类间前向引用),
|
||
# 若边注册边处理方法,前向引用的类此刻尚未注册 → 形参注解裸名解析落空 → fail-fast。
|
||
# ============================================================
|
||
for i1 in range(n):
|
||
child1: ast.AST* = ch.get(i1)
|
||
if child1 is None:
|
||
continue
|
||
if child1.kind() != ast.ASTKind.ClassDef:
|
||
continue
|
||
class_cd1: ast.ClassDef* = ast.ClassDef*(child1)
|
||
if class_cd1 is None or class_cd1.name is None:
|
||
continue
|
||
cls_name_v1: str = class_cd1.name
|
||
# 泛型类跳过(与第一遍 A 同判据)
|
||
tp_stub1: list[str]* = class_cd1.type_params
|
||
if tp_stub1 is not None and len(tp_stub1) > 0:
|
||
continue
|
||
cls_children1: list[ast.AST*]* = class_cd1.children
|
||
if cls_children1 is None:
|
||
continue
|
||
cn21: sizet = len(cls_children1)
|
||
for ci21 in range(cn21):
|
||
m_child1: ast.AST* = cls_children1.get(ci21)
|
||
if m_child1 is None:
|
||
continue
|
||
if m_child1.kind() != ast.ASTKind.FunctionDef:
|
||
continue
|
||
m_fd1: ast.FunctionDef* = ast.FunctionDef*(m_child1)
|
||
if m_fd1 is None or m_fd1.name is None:
|
||
continue
|
||
# App 预注册跳过方法 declare(原因见上方 pre_skip_fn_declares 注释)。
|
||
if pre_skip_fn_declares == 0:
|
||
_inject_method_declare(pool, mod, m_fd1, cls_name_v1, trans,
|
||
src_sha1, imported_modules, from_imports,
|
||
i32_ty, i8_ptr_ty, funcs_ptr, max_funcs)
|
||
|
||
i: sizet
|
||
for i in range(n):
|
||
child: ast.AST* = ch.get(i)
|
||
if child is None:
|
||
continue
|
||
kd: int = child.kind()
|
||
|
||
# FunctionDef → 提取函数名、参数、返回类型 → 注入 declare
|
||
if kd == ast.ASTKind.FunctionDef:
|
||
fd: ast.FunctionDef* = ast.FunctionDef*(child)
|
||
if fd is None or fd.name is None:
|
||
continue
|
||
# App 预注册跳过函数 declare(原因见上方 pre_skip_fn_declares 注释):
|
||
# 其形参/返回注解可能引用本遍尚未注册的类,resolve 落空会 fail-fast
|
||
# 打断多遍收敛(现场 Translator.vpi 的 InjectTracebackInstallCtor
|
||
# 形参 rawptr[Translator])。
|
||
if pre_skip_fn_declares != 0:
|
||
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* = ast.AnnAssign*(child)
|
||
if aa is None:
|
||
continue
|
||
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
|
||
dbg_tn: ast.Name* = ast.Name*(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* = ast.Name*(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* = ast.Constant*(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: int64 = 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
|
||
|
||
|
||
# ============================================================
|
||
# 依赖 vpi 按需注入(包 __init__ 重导出与子模块 stub 互相等待问题)
|
||
#
|
||
# LoadDepVpiIntoModule 只按 imported_modules 注入**一层**依赖 vpi、不递归;包 __init__.vpi 自身不含 class 定义(类全部来自 `from .sub import X` 重导出),
|
||
# 而重导出注册要求源模块 stub 已注入并以**定义文件 sha1** 注册过 entry ⇒ 两者互相等待 ⇒ 包内类都注册不上 ⇒ 包 stub 中引用这些类的注解裸名解析落空 → fail-fast。
|
||
#
|
||
# 故在重导出注册发现「源 entry 为空」时,按需把 sub_sha1 的 vpi 注入当前模块再重试注册(注入过程递归触发同一逻辑,依赖闭包自动补齐,与加载顺序无关)。
|
||
# 防重入表按 sha1 去重(环直接短路);注入失败不改语义,照原逻辑 return 0,不把可恢复问题升级为 fatal。
|
||
# ============================================================
|
||
VPI_INJ_MAX: t.CDefine = 1024
|
||
_vpi_inj_sha1s: bytes = None
|
||
_vpi_inj_count: int = 0
|
||
_vpi_inj_depth: int = 0
|
||
|
||
|
||
def ResetVpiInjectVisited() -> None:
|
||
"""清空按需注入的防重入表(每个模块的 stub 注入会话开始前调用)
|
||
|
||
表必须按"模块注入会话"重置:注入目标 mod 每个模块都不同,跨模块沿用
|
||
会让第二个模块命中缓存而拿不到本模块需要的 declare。
|
||
"""
|
||
global _vpi_inj_sha1s, _vpi_inj_count, _vpi_inj_depth
|
||
if _vpi_inj_sha1s is not None:
|
||
stdlib.free(_vpi_inj_sha1s)
|
||
_vpi_inj_sha1s = None
|
||
_vpi_inj_count = 0
|
||
_vpi_inj_depth = 0
|
||
|
||
|
||
def _ensure_dep_vpi_injected(pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
dep_sha1: str,
|
||
trans: HT.Translator*) -> int:
|
||
"""按需注入 dep_sha1 对应的 .vpi 到当前模块(防重入),1=就绪 / 0=失败"""
|
||
global _vpi_inj_sha1s, _vpi_inj_count, _vpi_inj_depth
|
||
if pool is None or mod is None or dep_sha1 is None or trans is None:
|
||
return 0
|
||
if dep_sha1[0] == '\0':
|
||
return 0
|
||
if _vpi_inj_sha1s is None:
|
||
_vpi_inj_sha1s = stdlib.malloc(VPI_INJ_MAX * 17)
|
||
if _vpi_inj_sha1s is None:
|
||
return 0
|
||
string.memset(_vpi_inj_sha1s, 0, VPI_INJ_MAX * 17)
|
||
_vpi_inj_count = 0
|
||
# 已注入过 / 正在注入(环)→ 视为就绪,直接短路
|
||
ii: int
|
||
for ii in range(_vpi_inj_count):
|
||
if string.strcmp(_vpi_inj_sha1s + sizet(ii) * 17, dep_sha1) == 0:
|
||
return 1
|
||
if _vpi_inj_count >= VPI_INJ_MAX or _vpi_inj_depth >= 32:
|
||
return 0
|
||
temp_dir: str = GenApi.get_temp_dir()
|
||
if temp_dir is None:
|
||
return 0
|
||
td_len_i: sizet = string.strlen(temp_dir)
|
||
sh_len_i: sizet = string.strlen(dep_sha1)
|
||
sub_i: str = Config.slice_subdir(dep_sha1, Config.Sha1SliceLevel)
|
||
sub_len_i: sizet = 0
|
||
if sub_i is not None:
|
||
sub_len_i = string.strlen(sub_i)
|
||
path_len_i: sizet = td_len_i + sub_len_i + sh_len_i + 8
|
||
path_i: bytes = stdlib.malloc(path_len_i)
|
||
if path_i is None:
|
||
return 0
|
||
if sub_len_i > 0:
|
||
viperlib.snprintf(path_i, path_len_i, "%s/%s/%s.vpi", temp_dir, sub_i, dep_sha1)
|
||
else:
|
||
viperlib.snprintf(path_i, path_len_i, "%s/%s.vpi", temp_dir, dep_sha1)
|
||
vf_i: rawptr[fileio.File] = fileio.File(path_i, fileio.MODE.R)
|
||
if vf_i.closed:
|
||
stdlib.free(path_i)
|
||
return 0
|
||
vf_i.close()
|
||
# 先登记再注入:注入过程中的递归遇到同 sha1 直接短路(防环)
|
||
my_idx_i: int = _vpi_inj_count
|
||
string.strcpy(_vpi_inj_sha1s + sizet(my_idx_i) * 17, dep_sha1)
|
||
_vpi_inj_count += 1
|
||
_vpi_inj_depth += 1
|
||
ret_i: int = ParseVpiIntoModule(pool, mod, path_i, trans)
|
||
_vpi_inj_depth -= 1
|
||
stdlib.free(path_i)
|
||
if ret_i != 1:
|
||
# 注入失败:**撤销登记**(把表尾项搬到本槽,保持 LIFO 语义),
|
||
# 否则后续对同一 sha1 的请求会被永久短路 → 该类永远注册不上,
|
||
# 错误却表现为远处注解解析失败。
|
||
_vpi_inj_count -= 1
|
||
if my_idx_i != _vpi_inj_count:
|
||
string.strcpy(_vpi_inj_sha1s + sizet(my_idx_i) * 17,
|
||
_vpi_inj_sha1s + sizet(_vpi_inj_count) * 17)
|
||
return 0
|
||
return 1
|
||
|
||
|
||
# ============================================================
|
||
# _inject_reexport_class_declare - 包重导出类以包 sha1 注册别名 entry
|
||
#
|
||
# 场景:源码 `import ast` 后写 `ast.Attribute`(模块限定名),查找端按「__init__ 包回退」解析为包 sha1,但 Attribute 定义在 ast/exprs.vp 并以定义文件 sha1 注册
|
||
# → find_struct_by_module 的 name+sha1 双精确匹配错位 fail-fast。本函数在处理包 __init__.vpi 的 `from .exprs import Attribute` 重导出行时调用:
|
||
# 复用子模块已注册的 entry(Ty/Fields/父类/虚表)以包 sha1 追加别名注册,使 ast.Attribute 按包归属可解析;非类名 find_struct_by_module 查不到自然跳过。
|
||
# 仅包 __init__ 触发,避免把普通模块的跨模块 from-import 错误归属到当前模块 sha1。
|
||
#
|
||
# Args: src_name 子模块中的原类名;local_name 包命名空间中的名字(或 as 别名);sub_sha1 子模块(定义文件)sha1;pkg_sha1 当前包 __init__ 的 sha1;trans 翻译器对象(源 entry 缺失时按需注入子模块 vpi 需要)
|
||
# ============================================================
|
||
def _inject_reexport_class_declare(pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
src_name: str,
|
||
local_name: str,
|
||
sub_sha1: str,
|
||
pkg_sha1: str,
|
||
trans: HT.Translator*) -> int:
|
||
"""把包子模块重导出的类以包 sha1 注册别名 entry(方案A:ast.Attribute 归属 ast 包)"""
|
||
if pool is None or mod is None or src_name is None or local_name is None:
|
||
return 0
|
||
if sub_sha1 is None or sub_sha1[0] == '\0' or pkg_sha1 is None or pkg_sha1[0] == '\0':
|
||
return 0
|
||
if src_name[0] == '\0' or local_name[0] == '\0':
|
||
return 0
|
||
# 幂等:包 sha1 下 local_name 已注册则跳过(重复 import 不重复注册)
|
||
if GenApi.find_struct_by_module(local_name, pkg_sha1) is not None:
|
||
return 1
|
||
# 找子模块源 entry(仅类有 entry;函数/常量查不到自然跳过)
|
||
src_entry: GenTypes.StructEntry* = GenApi.find_struct_by_module(src_name, sub_sha1)
|
||
if src_entry is None or src_entry.Ty is None:
|
||
# 源模块 stub 尚未注入(包 __init__ 与子模块互相等待,见
|
||
# _ensure_dep_vpi_injected 顶部说明):按需注入 sub_sha1 的 vpi 后
|
||
# 重试一次。失败照原逻辑跳过(不把可恢复问题升级为 fatal)。
|
||
if _ensure_dep_vpi_injected(pool, mod, sub_sha1, trans) != 0:
|
||
src_entry = GenApi.find_struct_by_module(src_name, sub_sha1)
|
||
if src_entry is None or src_entry.Ty is None:
|
||
return 0
|
||
# 注册别名 entry(绕过 register_struct 的 Ty 去重)
|
||
if GenApi.register_struct_alias(pool, local_name, pkg_sha1, src_entry) is None:
|
||
return 0
|
||
GenApi.add_visible_struct(pool, local_name)
|
||
return 1
|
||
|
||
|
||
# ============================================================
|
||
# _vpi_tattr_name / _vpi_class_has_packed - vpi 类装饰器 packed 判定
|
||
#
|
||
# vpi 存根保留 @c.Attribute(t.attr.packed) 装饰器(Converter 原样输出)。
|
||
# 跨模块 stub 注入 struct 时必须同步 packed 标记(<{...}>),否则引用
|
||
# 模块按非 packed 布局 GEP,与定义侧 packed 布局错位(B5)。
|
||
# ============================================================
|
||
def _vpi_tattr_name(node: ast.AST*, attr_name: str) -> int:
|
||
"""从 t.attr.<name> / t.attr.<name>(args) 提取最外层属性名并比较"""
|
||
if node is None:
|
||
return 0
|
||
if node.kind() == ast.ASTKind.Call:
|
||
cl2: ast.Call* = ast.Call*(node)
|
||
if cl2.func is None:
|
||
return 0
|
||
return _vpi_tattr_name(cl2.func, attr_name)
|
||
if node.kind() == ast.ASTKind.Attribute:
|
||
at2: ast.Attribute* = ast.Attribute*(node)
|
||
if at2.attr is None:
|
||
return 0
|
||
return 1 if string.strcmp(at2.attr, attr_name) == 0 else 0
|
||
return 0
|
||
|
||
|
||
def _vpi_class_has_packed(decorator_list: list[ast.AST*]*) -> int:
|
||
"""检查 vpi ClassDef 装饰器列表是否含 t.attr.packed"""
|
||
if decorator_list is None:
|
||
return 0
|
||
dn: sizet = len(decorator_list)
|
||
if dn == 0:
|
||
return 0
|
||
for di in range(dn):
|
||
deco: ast.AST* = decorator_list.get(di)
|
||
if deco is None:
|
||
continue
|
||
if deco.kind() == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(deco)
|
||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute:
|
||
continue
|
||
at0: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0:
|
||
continue
|
||
if cl.args is None:
|
||
continue
|
||
an: sizet = len(cl.args)
|
||
for ai in range(an):
|
||
ag: ast.AST* = cl.args.get(ai)
|
||
if ag is not None and _vpi_tattr_name(ag, "packed") != 0:
|
||
return 1
|
||
else:
|
||
if _vpi_tattr_name(deco, "packed") != 0:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _vpi_class_is_renum(class_cd: ast.ClassDef*) -> int:
|
||
"""检查 vpi ClassDef 是否为 REnum(基类含 t.REnum),返回 1=是 / 0=否
|
||
|
||
vpi 文本保留原始基类(`class LLVMType(t.REnum):`),故可据此识别。
|
||
REnum 布局 {i32 __tag, <max_variant_payload>} 由 ClassGenEnum.
|
||
_translate_renum_def 在正式翻译时按变体计算;vpi 侧按普通 struct 注册
|
||
会得到错误布局(变体字段都在嵌套类里,直接 AnnAssign 扫描得到 0 字段)。
|
||
"""
|
||
if class_cd is None or class_cd.bases is None:
|
||
return 0
|
||
bases: list[ast.AST*]* = class_cd.bases
|
||
bn: sizet = len(bases)
|
||
for bi in range(bn):
|
||
base_node: ast.AST* = bases.get(bi)
|
||
if base_node is None:
|
||
continue
|
||
bk: int = base_node.kind()
|
||
# Attribute(t.REnum) 形式
|
||
if bk == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||
if at.attr is not None and string.strcmp(at.attr, "REnum") == 0:
|
||
return 1
|
||
# Name(REnum) 形式(from t import REnum)
|
||
elif bk == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(base_node)
|
||
if nm.id is not None and string.strcmp(nm.id, "REnum") == 0:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# _inject_class_struct_declare - 从 vpi ClassDef 注册类为 struct 类型
|
||
#
|
||
# vpi 注入若只注入类内方法 declare 而不注册类本身,则严格模式下同一 vpi 内后续 AnnAssign 的裸类名注解(如 CArray 的元素 gdt_entry)is_struct_visible=0
|
||
# → resolve_annotation_type 返回 None → CArray 元素解析失败 fatal。故此处补齐源模块编译时 ClassGenDef.translate_class_def 的注册动作:
|
||
# 1) 已注册(find_struct_by_module 按 name+sha1 命中)→ 仅 add_visible_struct 标记当前文件可见;
|
||
# 2) 未注册 → 解析字段类型 → 创建命名 StructType("sha1.ClassName") → register_struct + add_visible_struct。
|
||
# 字段解析失败不 fatal(不注册该类,留给源模块编译注册,后续裸类名引用由调用侧 fail-fast 暴露)。
|
||
# ============================================================
|
||
def _inject_class_struct_declare(pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
class_cd: ast.ClassDef*,
|
||
cls_name: str,
|
||
trans: HT.Translator*,
|
||
src_sha1: str,
|
||
imported_modules: str,
|
||
from_imports: str,
|
||
name_prefix: str = None) -> int:
|
||
"""从 vpi ClassDef 注册类为 struct 类型(含字段),使同 vpi 内裸类名注解可解析
|
||
|
||
name_prefix:非 None 表示"本类是 prefix 类的嵌套类",注册名取扁平名
|
||
`{prefix}_{cls_name}`(多层嵌套自然递归为 A_B_C)——与定义端
|
||
ClassGenDef._translate_nested_classdefs 的命名口径逐字一致。
|
||
"""
|
||
if pool is None or mod is None or class_cd is None or cls_name is None:
|
||
return 0
|
||
# 扁平名(嵌套类):`{外类}_{内类}`。父类未注册时也照常登记(本函数自带
|
||
# "字段解析失败即不注册"的既有口径,交给多遍收敛)。
|
||
real_name: str = cls_name
|
||
if name_prefix is not None and name_prefix[0] != '\0':
|
||
p_len: sizet = string.strlen(name_prefix)
|
||
c_len: sizet = string.strlen(cls_name)
|
||
flat_buf_c: str = pool.alloc(p_len + 1 + c_len + 1)
|
||
if flat_buf_c is not None:
|
||
string.strcpy(flat_buf_c, name_prefix)
|
||
flat_buf_c[p_len] = '_'
|
||
string.strcpy(flat_buf_c + p_len + 1, cls_name)
|
||
real_name = flat_buf_c
|
||
# REPL(ReplStubLayoutMode,见文件末"REPL:includes 类完整布局与虚表恢复"):
|
||
# 本类若已由 .stub.ll 驱动的路径接管 ⇒ 这里整类让位——已登记(或本遍就地
|
||
# 登记成功)只标可见;父类未就绪 ⇒ 本遍不登记(收尾遍补齐);布局不可信 ⇒
|
||
# 不登记(片段侧字段访问明确失败,绝不按错偏移静默访问)。
|
||
# State=EMPTY 的类(无父类/无虚表/字段类型不可解析)不在此列,仍走下方既有口径。
|
||
if ReplStubLayoutMode != 0:
|
||
se: rawptr[ReplStubClass] = _repl_stub_find(
|
||
_repl_stub_key(pool, src_sha1, real_name))
|
||
if se is not None and se.State != _REPL_SS_EMPTY:
|
||
if se.State == _REPL_SS_PENDING:
|
||
_repl_stub_try(se, mod)
|
||
if se.State == _REPL_SS_OK:
|
||
GenApi.add_visible_struct(pool, real_name)
|
||
return 1
|
||
# 1. 已注册检查(含源模块编译时注册的 entry):仅标记可见,避免重复注册。
|
||
# 例外:**前向声明占位**(IsForwardDecl != 0)不早退——必须走下方注册路径,
|
||
# 并用「占位 Ty 原地写入」把字段体填进**同一个 Ty 对象**(见下方 fill_fwd_c 段)。
|
||
# existing_c/fill_fwd_c 声明在 if 外:下方填充段要跨块读取。
|
||
existing_c: GenTypes.StructEntry* = None
|
||
fill_fwd_c: int = 0
|
||
if src_sha1 is not None and src_sha1[0] != '\0':
|
||
existing_c = GenApi.find_struct_by_module(real_name, src_sha1)
|
||
if existing_c is not None and existing_c.Ty is not None:
|
||
if existing_c.IsForwardDecl != 0:
|
||
fill_fwd_c = 1
|
||
else:
|
||
GenApi.add_visible_struct(pool, real_name)
|
||
return 1
|
||
# REnum 类(class X(t.REnum))跳过:布局 {i32 __tag, <payload>} 由
|
||
# ClassGenEnum._translate_renum_def 按变体计算;vpi 侧按普通 struct 注册
|
||
# 会得到错误布局(变体字段都在嵌套类里,直接 AnnAssign 扫描得 0 字段)。
|
||
# 留给源模块正式翻译注册。LLVMType 本就因 field_count==0 早退,此守卫把
|
||
# 该行为显式化,避免将来 REnum 带直接字段时被误注册为普通 struct。
|
||
if _vpi_class_is_renum(class_cd) != 0:
|
||
return 0
|
||
# 规则 5.5 的证据前置(**必须早于**下方字段注解解析):
|
||
# 本函数在**注入期**解析字段注解(GenApi.resolve_annotation_type →
|
||
# _norm_type_arg_name),此刻被注入的类尚未 register_struct,且注入期**没有
|
||
# "类定义窗口"**(CurDefModuleSha1=None)⇒ 规则 5.5 的入口闸门
|
||
# (`_def_sha1 is None and _def_mod is not None`)整条关闭、四条证据一条都查不到
|
||
# ⇒ 注解里的裸类名实参取不到定义模块 SHA1(如 `children: list[rawptr[AST]]`
|
||
# 里的 AST)⇒ 特化名被冻结成**裸名版本**,与该类在自身翻译期(类已注册)解析出的
|
||
# 带前缀形式分裂成两个特化名,最终以"非基本类型指针不可隐式转换"报出。现场:
|
||
# includes/ast/base.vp:359 `_append_child(self.children, node, self.pool)`
|
||
# 源 %"…list[rawptr[AST]]"* vs 目标 %"…list[rawptr<定义模块SHA1>.AST]"*。
|
||
# mark_module_class 一次调用同时完成两件事:
|
||
# ① 登记"{src_sha1}.{real_name}"(名字归属,与 struct 注册状态无关);
|
||
# ② 把 CurDefModuleSha1/CurDefClassName 指向本次注入的模块与类(规则 5.5 的闸门)。
|
||
GenApi.mark_module_class(pool, src_sha1, real_name)
|
||
# 1.5 嵌套类 / 嵌套 struct:vpi 里的嵌套类是**缩进 ClassDef**(Converter 原样写出,
|
||
# 见 Converter._PyiClass 的嵌套类分支),定义端以扁平名 `{外类}_{内类}` 发射
|
||
# (ClassGenDef._translate_nested_classdefs)⇒ 此处按同一命名**先**递归登记。
|
||
# 必须早于下方字段注解解析:外层字段可能写成 `外类.内类`,只有扁平名条目已在
|
||
# 结构体表里,TypeGenResolve._resolve_nested_class_type 才解析得出类型。
|
||
_inject_nested_class_declares(pool, mod, class_cd, real_name, trans,
|
||
src_sha1, imported_modules, from_imports)
|
||
# 还原"正在定义类"窗口:嵌套递归内的 mark_module_class 会把 CurDefClassName 改成
|
||
# 嵌套类名(A_B)。不还原会让外层字段注解的规则 5.5 自引用证据判错(裸类名票名冻结
|
||
# 成裸名版本、与定义侧带前缀形式分裂),也会让 `外类.内类` 的守卫①(CurDefClassName
|
||
# == 外类名)失效。
|
||
GenApi.mark_module_class(pool, src_sha1, real_name)
|
||
# 2. 收集字段类型(AnnAssign 子节点,仿 ClassGenDef.translate_class_def)
|
||
field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||
field_types_buf: sizet* = 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*]* = class_cd.children
|
||
if cls_children_c is not None:
|
||
cn_c: sizet = len(cls_children_c)
|
||
for ci_c in range(cn_c):
|
||
m_child_c: ast.AST* = 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* = ast.AnnAssign*(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* = ast.Name*(m_aa_c.target)
|
||
if m_tgt_c.id is None:
|
||
continue
|
||
field_ty_c: llvmlite.LLVMType* = 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] = sizet(m_tgt_c.id)
|
||
field_types_buf[field_count] = sizet(field_ty_c)
|
||
field_count += 1
|
||
# 3. vpi 存根不含类字段(只序列化 class 名 + def 签名)→ field_count 恒为 0;注册空类会让源模块编译时 find_struct_by_module 命中空类
|
||
# → "结构体已注册但字段数为0" InternalCompilerError。故改为不注册:依赖模块已编译时命中真实类(上方已处理),未编译时由 _deps_pending 多遍收敛。
|
||
if field_count == 0:
|
||
return 0
|
||
# 4. 构建 ParamNode 链表
|
||
first_node_c: rawptr[llvmlite.ParamNode] = None
|
||
prev_node_c: rawptr[llvmlite.ParamNode] = None
|
||
for fi_c in range(field_count):
|
||
fty_c: llvmlite.LLVMType* = (llvmlite.LLVMType*)(field_types_buf[fi_c])
|
||
pnode_c: rawptr[llvmlite.ParamNode] = 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 一致)。
|
||
# 嵌套类用扁平名 real_name(`外类_内类`),与定义端
|
||
# ClassGenDef._translate_nested_classdefs 发射的 "sha1.外类_内类" 逐字一致。
|
||
type_name_c: str = real_name
|
||
if src_sha1 is not None and src_sha1[0] != '\0':
|
||
# [D7·截断纪律] 旧实现固定 64 缓冲 + snprintf("%s.%s"):sha1(16)+'.' 占 17
|
||
# 字节,类名超过 46 字符即被**静默截断** ⇒ 命名结构体名与定义侧
|
||
# ClassGenDef 的 "sha1.ClassName" 不一致 → find_struct_by_module /
|
||
# lookup_field_by_class 查不到(误判"未注册")。改为精确长度分配。
|
||
sh_len_c: sizet = string.strlen(src_sha1)
|
||
cn_len_c: sizet = string.strlen(real_name)
|
||
name_buf_c: str = pool.alloc(sh_len_c + 1 + cn_len_c + 1)
|
||
if name_buf_c is not None:
|
||
string.strcpy(name_buf_c, src_sha1)
|
||
name_buf_c[sh_len_c] = '.'
|
||
string.strcpy(name_buf_c + sh_len_c + 1, real_name)
|
||
type_name_c = name_buf_c
|
||
struct_ty_c: llvmlite.LLVMType* = 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* = llvmlite.DeepCopyType(
|
||
CompilerFactory.factory.PermPool, struct_ty_c)
|
||
if st_copy_c is not None:
|
||
struct_ty_c = st_copy_c
|
||
# 5.5 前向声明占位命中 ⇒ **占位 Ty 原地写入**(与 ClassGenDef.translate_class_def
|
||
# 的 fill_fwd 段逐句同款):给已存在的类型对象 `*existing.Ty` 赋入刚构建的
|
||
# 字段体,并让 struct_ty_c 指向**同一个 Ty**。
|
||
# 这是"不新建条目"的唯一正确做法:若此处让 struct_ty_c 保持新对象,下方
|
||
# register_struct 按 **Ty 指针**去重就会 miss ⇒ 每一遍注入都追加一个新条目
|
||
# ⇒ StructTable 爆表(现场:table full, cannot register Translator)。
|
||
# 必须早于 module_add_named_type:避免同一名字先以占位名注册、又以新对象重复注册。
|
||
if fill_fwd_c != 0 and existing_c is not None and existing_c.Ty is not None:
|
||
fwd_name_c: str = llvmlite.get_struct_name(existing_c.Ty)
|
||
fwd_fields_c: rawptr[llvmlite.ParamNode] = llvmlite.get_struct_fields(struct_ty_c)
|
||
fwd_fc_c: int = llvmlite.get_struct_fcount(struct_ty_c)
|
||
*existing_c.Ty = llvmlite.LLVMType.Struct(fwd_fields_c, fwd_fc_c, fwd_name_c)
|
||
struct_ty_c = existing_c.Ty
|
||
type_name_c = fwd_name_c
|
||
existing_c.IsForwardDecl = 0
|
||
existing_c.FieldCount = 0
|
||
# 6. 注册到当前模块(输出类型定义行)+ 全局 StructTable + 可见性
|
||
llvmlite.module_add_named_type(mod, pool, struct_ty_c)
|
||
# B5:vpi 保留 @c.Attribute(t.attr.packed) 装饰器,stub 注入的跨模块
|
||
# struct 必须同样 packed(<{...}>),否则引用模块 GEP 按非 packed 布局
|
||
# 算偏移,与定义侧 packed 布局错位(gdt/elf/fat32 二进制格式字段错读)。
|
||
if _vpi_class_has_packed(class_cd.decorator_list) != 0:
|
||
llvmlite.module_set_named_type_packed(mod, pool, struct_ty_c, 1)
|
||
# 注册名用扁平名 real_name:跨模块引用 `外类.内类` 时 TypeGenResolve 查的是
|
||
# 扁平名条目(与定义端 StructGen 的登记名同源)。
|
||
entry_c: GenTypes.StructEntry* = GenApi.register_struct(
|
||
pool, real_name, struct_ty_c, src_sha1)
|
||
if entry_c is None:
|
||
return 0
|
||
# B5:vpi 注入注册的 entry 同步 IsPacked——后续跨模块 stub 注入
|
||
# (ensure_struct_def_in_module)按 entry.IsPacked 判定 packed,否则读 0
|
||
# → 注入的跨模块 struct 非 packed(GEP 布局错位)。
|
||
if _vpi_class_has_packed(class_cd.decorator_list) != 0:
|
||
entry_c.IsPacked = 1
|
||
# vpi 注入类时同步注册字段到 StructEntry 字段表(名字+类型):仅设 Ty 而字段表空(FieldCount=0)会让 lookup_field/lookup_field_by_class 按名查字段失败
|
||
# → 属性访问走 _deps_pending → includes 无重译 → 失败版本落盘恒 0(如 File.size 的 li.QuadPart 恒 0 → read_all=0)。
|
||
# add_field 内部按 StdlibAlloc 复制字段名到 perm_pool,跨 reset 存活。
|
||
afi_c: int = 0
|
||
for afi_c in range(field_count):
|
||
af_name_c: str = str(field_names_buf[afi_c])
|
||
af_ty_c: llvmlite.LLVMType* = (llvmlite.LLVMType*)(field_types_buf[afi_c])
|
||
GenApi.add_field(pool, entry_c, af_name_c, af_ty_c)
|
||
GenApi.add_visible_struct(pool, real_name)
|
||
return 1
|
||
|
||
|
||
# ============================================================
|
||
# _inject_nested_class_declares - 递归登记 vpi 类里的嵌套类
|
||
#
|
||
# vpi 里的嵌套类是缩进的 ClassDef(Converter._PyiClass 原样写出),定义端以
|
||
# 扁平名 `{外类}_{内类}` 发射(ClassGenDef._translate_nested_classdefs)⇒ 注入侧
|
||
# 必须按同一命名登记,跨模块引用 `外类.内类` 才解析得出类型(否则与 TPC 侧
|
||
# stub.ll 的递归声明不对齐,引用模块拿到 None)。
|
||
#
|
||
# 多层嵌套靠递归自然展开(A.B.C → prefix=A_B、cls=C ⇒ A_B_C)。这里**不**过滤
|
||
# REnum / 空字段:把判定统一交给 _inject_class_struct_declare 既有的守卫
|
||
# (REnum 跳过、field_count==0 不注册、已注册只标可见)。
|
||
# 每个内层类登记后,_inject_class_struct_declare 会把自己的
|
||
# mark_module_class 窗口留在嵌套类上;故外层调用点在其后**再调一次**
|
||
# mark_module_class 还原(见 _inject_class_struct_declare 内注释)。
|
||
# ============================================================
|
||
def _inject_nested_class_declares(pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
class_cd: ast.ClassDef*,
|
||
prefix_name: str,
|
||
trans: HT.Translator*,
|
||
src_sha1: str,
|
||
imported_modules: str,
|
||
from_imports: str) -> None:
|
||
"""递归登记 class_cd 的所有嵌套 ClassDef 为扁平名类型(前缀 = prefix_name)"""
|
||
if pool is None or mod is None or class_cd is None or prefix_name is None:
|
||
return
|
||
children: list[ast.AST*]* = class_cd.children
|
||
if children is None:
|
||
return
|
||
cn: sizet = len(children)
|
||
for ci in range(cn):
|
||
child_ast: ast.AST* = children.get(ci)
|
||
if child_ast is None:
|
||
continue
|
||
if child_ast.kind() != ast.ASTKind.ClassDef:
|
||
continue
|
||
child_cd: ast.ClassDef* = ast.ClassDef*(child_ast)
|
||
if child_cd is None or child_cd.name is None or child_cd.name[0] == '\0':
|
||
continue
|
||
_inject_class_struct_declare(pool, mod, child_cd, child_cd.name, trans,
|
||
src_sha1, imported_modules, from_imports,
|
||
prefix_name)
|
||
|
||
|
||
# ============================================================
|
||
# _ensure_full_value_types - 跨模块「值类型」引用注册完整定义
|
||
#
|
||
# opaque 不足以支撑 llc:值 struct global(@_per_cpu = global %per_cpu_data)、[N x opaque] 数组(@gdt = global [7 x %gdt_entry])、值 struct 返回类型
|
||
# 均报 "use of undefined type"(值类型大小需已知)。vpi 注入类已 add_field 含字段(完整 Ty),在注册表可查时注册完整定义;
|
||
# 指针引用(Ptr(Struct))无需——_scan_type_for_cross_module_refs 的 Ptr 分支添加 opaque 即可。
|
||
# ============================================================
|
||
def _ensure_full_value_types(mod: llvmlite.LLVMModule*,
|
||
pool: memhub.MemBuddy*,
|
||
ty: llvmlite.LLVMType*) -> None:
|
||
"""递归为值 struct / 数组元素 struct 注册完整定义(递归深度守卫)"""
|
||
if mod is None or pool is None or ty is None: return
|
||
match ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
full_se_h: GenTypes.StructEntry* = GenApi.find_struct_by_type(ty)
|
||
if full_se_h is not None and full_se_h.Ty is not None:
|
||
llvmlite.module_add_named_type(mod, pool, full_se_h.Ty)
|
||
# B5:值类型注入同步 packed 标记(对齐 ensure_struct_def_in_module)——
|
||
# vpi 全局变量(@_per_cpu = global %per_cpu_data)/值返回类型注入完整
|
||
# 定义时若只 add 不 set_packed → 引用模块节点 IsPacked=0 → 输出 {…}
|
||
# 非 packed(GEP 与定义侧 packed 布局错位,stub packed 失效根因)。
|
||
if full_se_h.IsPacked != 0:
|
||
llvmlite.module_set_named_type_packed(mod, pool, full_se_h.Ty, 1)
|
||
case llvmlite.LLVMType.Array(aelem_h, _):
|
||
if aelem_h is not None:
|
||
_ensure_full_value_types(mod, pool, aelem_h)
|
||
case _:
|
||
pass
|
||
|
||
|
||
# ============================================================
|
||
# _AggregateParamTy - 裸结构体(聚合)形参类型统一判据
|
||
#
|
||
# 「裸结构体形参」(注解是结构体/类名、无 `| t.CPtr`)的 LLVM 形参类型判据,三处必须同步(同一文字、同一结果):
|
||
# 1) 定义侧 FuncGenDef.forward_declare_functions / translate_function_def 的 extern 声明分支;
|
||
# 2) declare 注入侧 _inject_function_declare / _inject_method_declare(本函数);
|
||
# 3) 调用侧:per-函数 found_func 走 ExprGenBase.coerce_to_type(指针 → 非指针值 build_load 解引用);跨模块名走 CallGenLib._coerce_args_to_def_params(按本判据得出的形参类型 coerce)。
|
||
# 规则(对齐 TPC「仅 IsStruct 吸收 1 层 CPtr」+「CStruct → 具名结构体值」):
|
||
# * C ABI 外部声明(t.CExtern/t.State,本模块无 define 侧)→ 按值 %Struct:真实被调方在 DLL/CRT,形参按 C ABI 传值;
|
||
# 值参数要求具名类型有**完整定义**(opaque/未定义具名类型作值参数 ⇒ llc "invalid type for function argument")。判据 = find_struct_by_type 命中且非 opaque:
|
||
# 命中 → 先注册完整定义(module_add_named_type)再按值;真 opaque/未注册 → 降级 i64(不引用任何具名类型,恒合法;
|
||
# Windows x64 ABI 小结构体(≤8 字节)按整数寄存器传、位兼容,且与定义侧同样降级 ⇒ 同符号签名仍逐字一致)。
|
||
# * TPV 内部函数/方法(存在 define 侧)→ Ptr(struct):与定义侧逐字一致,指针目标只需 opaque 声明(module_ensure_opaque_for_type)。
|
||
# ============================================================
|
||
def _AggregateParamTy(mod: llvmlite.LLVMModule*,
|
||
pool: memhub.MemBuddy*,
|
||
ty: llvmlite.LLVMType*,
|
||
is_c_abi: int) -> llvmlite.LLVMType*:
|
||
"""裸结构体形参类型(C ABI 按值 / 内部 Ptr(struct),见上方注释)"""
|
||
if mod is None or pool is None or ty is None:
|
||
return ty
|
||
if is_c_abi != 0:
|
||
# 真 opaque 哨兵:fcount < 0(includes/llvmlite/__module.vp L491)
|
||
is_opaque_ap: int = 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Struct(_, fcount_ap, _):
|
||
if fcount_ap < 0:
|
||
is_opaque_ap = 1
|
||
case _:
|
||
pass
|
||
if is_opaque_ap == 0 and GenApi.find_struct_by_type(ty) is not None:
|
||
_ensure_full_value_types(mod, pool, ty)
|
||
return ty
|
||
return llvmlite.Int64(pool)
|
||
ptr_ty_ap: llvmlite.LLVMType* = llvmlite.Ptr(pool, ty)
|
||
llvmlite.module_ensure_opaque_for_type(mod, pool, ptr_ty_ap)
|
||
return ptr_ty_ap
|
||
|
||
|
||
# ============================================================
|
||
# _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 未注入)」。
|
||
# ============================================================
|
||
def _inject_global_variable_declare(pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
aa: ast.AnnAssign*,
|
||
trans: HT.Translator*,
|
||
imported_modules: str,
|
||
from_imports: str,
|
||
i32_ty: llvmlite.LLVMType*,
|
||
i8_ptr_ty: llvmlite.LLVMType*,
|
||
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* = aa.target
|
||
if tgt_v is None or tgt_v.kind() != ast.ASTKind.Name:
|
||
return
|
||
nm_v: ast.Name* = ast.Name*(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: sizet = 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* = 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* = 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* = 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),一律保持值则 @t.Object 类模块级变量按指针访问读错 vtable(#PF)。
|
||
gv_entry_v: GenTypes.StructEntry* = 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)。
|
||
# 值类型(struct 值 / 数组元素 struct)注册完整定义(见 _ensure_full_value_types)
|
||
_ensure_full_value_types(mod, pool, gv_ty_v)
|
||
llvmlite.module_ensure_opaque_for_type(mod, pool, gv_ty_v)
|
||
gv_v: llvmlite.GlobalVariable* = 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*,
|
||
mod: llvmlite.LLVMModule*,
|
||
fd: ast.FunctionDef*,
|
||
trans: HT.Translator*,
|
||
src_sha1: str,
|
||
imported_modules: str,
|
||
from_imports: str,
|
||
i32_ty: llvmlite.LLVMType*,
|
||
i8_ptr_ty: llvmlite.LLVMType*,
|
||
funcs_ptr: GenTypes.FuncEntry*,
|
||
max_funcs: int) -> None:
|
||
"""为 FunctionDef 创建 declare 声明并注入到模块"""
|
||
if fd.name is None:
|
||
return
|
||
|
||
# 自注入守卫(仅全量翻译阶段 _declare_only == 0):本模块自己定义的顶层函数,
|
||
# 其 define 由 FuncGenDef.forward_declare_functions + translate_function_def
|
||
# 负责发射;经依赖链把**本模块自己的** vpi 反向注入(循环 import)会在同一模块
|
||
# 里制造同名 declare,且注入侧类型推断与定义端可能不一致。
|
||
# 现场:json/__parser.vp ↔ json/__init__.vp 循环 import,__parser 编译时注入的
|
||
# ParseState.__init__ 取 i32(vpi 无返回注解 → i32 默认),而定义端是 void
|
||
# → llc: value doesn't match function result type 'i32'。
|
||
if trans is not None and trans._declare_only == 0 and trans.ModuleSha1 is not None:
|
||
if string.strcmp(src_sha1, trans.ModuleSha1) == 0:
|
||
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")
|
||
# 裸结构体形参判据(见 _AggregateParamTy):C ABI 外部声明(t.CExtern/
|
||
# t.State,本模块无 define 侧)按值;其余(含 t.CExport,其 define 侧在
|
||
# 本树内)保持 Ptr(struct)。与定义侧 FuncGenDef is_extern_decl 同源,
|
||
# 保证注入侧 declare 与定义侧 define 同口径(三处必须同步)。
|
||
agg_is_c_abi: int = 0
|
||
if has_extern != 0 or has_state != 0:
|
||
agg_is_c_abi = 1
|
||
|
||
# 计算 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)
|
||
|
||
# 本文件自己的裸名声明优先:extern/state/export 声明的符号**不带 SHA1 前缀**
|
||
# (与 FuncGenDef.vp:172-173 同口径),若本文件自己也声明了同名符号,先注入的
|
||
# 依赖版本会占用该名字 → forward_declare_functions 见名已存在即 continue
|
||
# (FuncGenDef.vp:178-182)→ 本文件自己的签名被静默丢弃,调用点改按依赖签名
|
||
# coerce。现场:includes/socket.vp 的
|
||
# `extern def connect(fd: u64, addr: SocketAddr | t.CPtr, addrlen: INT)`
|
||
# 被 w32.winsock2 的 `connect(..., WSASocketAddr | t.CPtr, ...)` 顶掉 →
|
||
# socket.vp:236 `connect(self.fd, c.Addr(addr), 16)` 报
|
||
# 「指针隐式转指针已禁止:SocketAddr* → WSASocketAddr*」。
|
||
# 判据只用**裸名**:带 SHA1 前缀的依赖函数名与本文件同名声明不会相互顶替。
|
||
if use_bare_name != 0 and GenApi.is_file_func(fd.name) != 0:
|
||
return
|
||
|
||
# 去重:如果函数已存在则跳过
|
||
existing: llvmlite.Function* = GenApi.find_func_in_module(mod, mangled_name)
|
||
if existing is not None:
|
||
return
|
||
# 也检查裸名(非裸名模式时,CExport 函数可能已通过其他途径注册)
|
||
if use_bare_name == 0:
|
||
existing_bare: llvmlite.Function* = 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* = 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:
|
||
# t.State/t.CExtern 是存储类装饰器标记,非真实返回类型。
|
||
# 注解 "HANDLE | t.State"(FindFirstFileA)含真实类型 → 不能归 Void
|
||
# (此前无条件 has_state→Void,extern declare 返回 void,运行期
|
||
# FindFirstFileA 句柄丢失 → 文件存在性判断失效根因)。
|
||
# 仅纯标记注解(t.State/t.CExtern/t.CExport 单独出现)才返回 Void。
|
||
is_pure_marker_rt: int = 0
|
||
rk2_rt: int = fd.returns.kind()
|
||
if rk2_rt == ast.ASTKind.Name or rk2_rt == ast.ASTKind.Constant:
|
||
is_pure_marker_rt = 1
|
||
elif rk2_rt == ast.ASTKind.Attribute:
|
||
pm_at: ast.Attribute* = ast.Attribute*(fd.returns)
|
||
if pm_at.attr is not None and (string.strcmp(pm_at.attr, "State") == 0
|
||
or string.strcmp(pm_at.attr, "CExtern") == 0
|
||
or string.strcmp(pm_at.attr, "CExport") == 0):
|
||
is_pure_marker_rt = 1
|
||
if is_pure_marker_rt != 0:
|
||
ret_ty = llvmlite.Void(pool)
|
||
elif has_state != 0 or has_extern != 0 or has_export != 0:
|
||
# 含真实类型但解析失败:回退外部函数返回类型白名单
|
||
# (FindFirstFileA/GetStdHandle → i8* HANDLE 等),避免默认 i32
|
||
# 截断指针(vpi 缓存/AST 解析 miss 时的稳健兜底)。
|
||
ret_ty = GenApi.infer_external_func_ret_ty(pool, fd.name)
|
||
# [S4 fail-fast] 有返回注解却解析不出类型 → 不再静默退 i32;
|
||
# 无注解(fd.returns is None)是合法默认,保持下方 i32。
|
||
if ret_ty is None and fd.returns is not None:
|
||
GenApi.fatal_error(
|
||
fd.returns, f"extern 声明 '{fd.name}' 返回类型注解解析失败",
|
||
"NameError")
|
||
# 默认返回 i32
|
||
if ret_ty is None:
|
||
ret_ty = i32_ty
|
||
|
||
# 值 struct 返回类型(如 gfx.vp Mat4_Identity -> Mat4)需要完整定义:
|
||
# declare %Mat4 opaque → llc "use of undefined type"。注册完整定义。
|
||
_ensure_full_value_types(mod, pool, ret_ty)
|
||
llvmlite.module_ensure_opaque_for_type(mod, pool, ret_ty)
|
||
|
||
# 创建 declare
|
||
func: llvmlite.Function* = llvmlite.create_declare(
|
||
pool, mod, mangled_name, ret_ty)
|
||
if func is None:
|
||
return
|
||
|
||
# 提取参数并添加到 declare
|
||
param_count: int = 0
|
||
fd_defaults: t.CPtr = None
|
||
fd_default_count: int = 0
|
||
if fd.args is not None:
|
||
args_node: ast.Arguments* = ast.Arguments*(fd.args)
|
||
if args_node.args is not None:
|
||
args_list: list[ast.AST*]* = 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, uint64(fd.args))
|
||
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印)
|
||
VLogger.repl_abort()
|
||
pi: sizet
|
||
for pi in range(param_count):
|
||
ag: ast.Arg* = ast.Arg*(args_list.get(pi))
|
||
if ag is None:
|
||
continue
|
||
pname: str = ag.arg
|
||
if pname is None:
|
||
pname = "arg"
|
||
# 解析参数类型
|
||
param_ty: llvmlite.LLVMType* = i8_ptr_ty
|
||
# 优先用 vpi 文本注解缓存解析参数类型(与返回类型同源——vpi
|
||
# tree 深层节点 kind() 可能损坏,AST resolve 得垃圾类型如
|
||
# sizet→i32,跨模块 declare 参数宽错 → 调用侧 trunc 传参、
|
||
# callee 按宽类型读栈残留垃圾高位 → 参数巨值崩溃(SlotsBugTest
|
||
# 根因)。段索引 = pi(模块级函数无 self,0=首参)。
|
||
pseg_ty: llvmlite.LLVMType* = _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(_, _, _):
|
||
# 裸结构体形参:C ABI 按值 / 内部 Ptr(struct)
|
||
# (判据与降级见 _AggregateParamTy,三处必须同步)
|
||
param_ty = _AggregateParamTy(mod, pool, pseg_ty, agg_is_c_abi)
|
||
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* = GenApi.resolve_annotation_type(
|
||
pool, ag.annotation, imported_modules, from_imports, trans)
|
||
if resolved_ty is not None:
|
||
# CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。
|
||
# 裸结构体参数 → _AggregateParamTy(同一判据)。
|
||
match resolved_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
param_ty = _AggregateParamTy(mod, pool, resolved_ty, agg_is_c_abi)
|
||
case llvmlite.LLVMType.Array(aelem_ty, _):
|
||
param_ty = llvmlite.Ptr(pool, aelem_ty)
|
||
case _:
|
||
param_ty = resolved_ty
|
||
else:
|
||
# [S4 fail-fast] 有注解却解析不出类型 → 不再静默退 i8*
|
||
# (无注解 ag.annotation is None 是合法默认,保持 i8*)
|
||
GenApi.fatal_error(
|
||
ag.annotation,
|
||
f"extern 声明 '{fd.name}' 形参 '{pname}' 类型注解解析失败",
|
||
"NameError")
|
||
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*]* = 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, None, src_sha1) == 0:
|
||
trans._func_count = cur_count + 1
|
||
# 补全全局函数表 Spec(参数已全部添加,对齐 FuncGenDef 行为):
|
||
# vpi 注入路径若不补全,跨模块 vtable 生成 find_func_entry_global()
|
||
# 时 Spec 为空 → "vtable 方法找不到函数类型"(依赖模块晚于本模块编译时必现)
|
||
GenApi.update_global_func_spec(fd.name, func)
|
||
|
||
|
||
# ============================================================
|
||
# _inject_method_declare - 为 ClassDef 内的方法创建 declare
|
||
#
|
||
# 方法混淆名 {src_sha1}.{ClassName}.{method};参数类型从 vpi 注解解析(self 无注解时默认为 i8*)。
|
||
# 注入正确签名可避免调用时 _ensure_method_declare 用实参类型创建错误 declare(如 size: sizet 被写成 i32 而非 i64)。
|
||
# ============================================================
|
||
def _inject_method_declare(pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
m_fd: ast.FunctionDef*,
|
||
cls_name: str,
|
||
trans: HT.Translator*,
|
||
src_sha1: str,
|
||
imported_modules: str,
|
||
from_imports: str,
|
||
i32_ty: llvmlite.LLVMType*,
|
||
i8_ptr_ty: llvmlite.LLVMType*,
|
||
funcs_ptr: GenTypes.FuncEntry*,
|
||
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
|
||
|
||
# 自注入守卫(仅全量翻译阶段 _declare_only == 0):本模块自己定义的类,其方法
|
||
# define 由 ClassGenVTable._translate_oop_methods 发射;把本模块自己的 vpi 经
|
||
# 依赖链反向注入(循环 import)会在同一模块制造同名 declare,且注入侧类型与
|
||
# 定义端可能不一致 —— 现场 json/__parser.vp 编译时其自身 vpi 被注入,
|
||
# ParseState.__init__ 注入成 i32(vpi 无返回注解 → i32 默认)而定义端是 void
|
||
# → llc: value doesn't match function result type 'i32'。
|
||
if trans is not None and trans._declare_only == 0 and trans.ModuleSha1 is not None:
|
||
if string.strcmp(src_sha1, trans.ModuleSha1) == 0:
|
||
return
|
||
|
||
# 构造方法查找名: ClassName.method
|
||
lookup_name_len: sizet = 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: sizet = 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* = 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* = 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* = GenApi.get_struct_type(cls_name)
|
||
if cls_ty is not None:
|
||
ret_ty = llvmlite.Ptr(pool, cls_ty)
|
||
# [S4 fail-fast] 有返回注解却解析不出类型 → 不再静默退 i32;
|
||
# __new__ 无注解、普通方法无注解均走下方 i32 合法默认。
|
||
if ret_ty is None and m_fd.returns is not None:
|
||
GenApi.fatal_error(
|
||
m_fd.returns,
|
||
f"extern 方法 '{cls_name}.{m_fd.name}' 返回类型注解解析失败",
|
||
"NameError")
|
||
# __init__ 无返回注解 → 与定义端一致取 Void(定义端
|
||
# ClassGenVTable._translate_method 对 __init__ 恒 Void)。若按"普通方法无注解"
|
||
# 退成下方 i32 默认,就会出现 declare i32 / define void 的返回类型分裂:
|
||
# 同模块内 llc 报 value doesn't match function result type,跨模块是静默 ABI 不一致。
|
||
if ret_ty is None and string.strcmp(m_fd.name, "__init__") == 0:
|
||
ret_ty = llvmlite.Void(pool)
|
||
if ret_ty is None:
|
||
ret_ty = i32_ty
|
||
|
||
# 创建 declare
|
||
func: llvmlite.Function* = llvmlite.create_declare(
|
||
pool, mod, mangled_name, ret_ty)
|
||
if func is None:
|
||
return
|
||
|
||
# 提取参数并添加到 declare
|
||
param_count: int = 0
|
||
md_defaults: t.CPtr = None
|
||
md_default_count: int = 0
|
||
if m_fd.args is not None:
|
||
args_node: ast.Arguments* = ast.Arguments*(m_fd.args)
|
||
if args_node.args is not None:
|
||
args_list: list[ast.AST*]* = 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, uint64(m_fd.args))
|
||
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印)
|
||
VLogger.repl_abort()
|
||
pi: sizet
|
||
for pi in range(param_count):
|
||
ag: ast.Arg* = ast.Arg*(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* = 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* = i8_ptr_ty
|
||
# 优先用 vpi 文本注解缓存解析参数类型(与返回类型同源——vpi
|
||
# tree 深层节点 kind() 可能损坏,AST resolve 得垃圾类型如
|
||
# sizet→i32,declare 参数宽错 → 调用侧 trunc i64→i32 传参、
|
||
# callee 按 i64 读栈残留垃圾高位 → can=巨值读未写槽位崩溃,
|
||
# SlotsBugTest 根因)。段索引 = pi(0=self 空段)。
|
||
pseg_ty: llvmlite.LLVMType* = _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(_, _, _):
|
||
# 裸结构体形参:方法恒为 TPV 内部符号(mangled =
|
||
# {sha1}.{Class}.{method},无 C ABI 外部 define),
|
||
# 故 is_c_abi=0 → Ptr(struct),与定义侧
|
||
# ClassGenVTable L744-750 逐字一致(三处必须同步)。
|
||
param_ty = _AggregateParamTy(mod, pool, pseg_ty, 0)
|
||
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* = GenApi.resolve_annotation_type(
|
||
pool, ag.annotation, imported_modules, from_imports, trans)
|
||
if resolved_ty is not None:
|
||
# CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。
|
||
# 裸结构体参数 → _AggregateParamTy(同一判据,is_c_abi=0)。
|
||
match resolved_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
param_ty = _AggregateParamTy(mod, pool, resolved_ty, 0)
|
||
case llvmlite.LLVMType.Array(aelem_ty, _):
|
||
param_ty = llvmlite.Ptr(pool, aelem_ty)
|
||
case _:
|
||
param_ty = resolved_ty
|
||
else:
|
||
# [S4 fail-fast] 有注解却解析不出类型 → 不再静默退 i8*
|
||
# (无注解 ag.annotation is None 是合法默认,保持 i8*)
|
||
GenApi.fatal_error(
|
||
ag.annotation,
|
||
f"extern 方法 '{cls_name}.{m_fd.name}' 形参 '{pname}' 类型注解解析失败",
|
||
"NameError")
|
||
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* = llvmlite.Int1(pool)
|
||
if i1_ty_si is not None:
|
||
i1_ptr_ty_si: llvmlite.LLVMType* = 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* = ast.Arguments*(m_fd.args)
|
||
if md_args_node.defaults is not None:
|
||
md_dlist: list[ast.AST*]* = 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, None, src_sha1) == 0:
|
||
trans._func_count = cur_count + 1
|
||
# 补全全局函数表 Spec(参数已全部添加,对齐 ClassGenVTable 行为):
|
||
# vpi 注入的方法声明若不补全 Spec,跨模块继承 vtable 生成时
|
||
# find_func_entry_global().Spec 为空 → "vtable 方法找不到函数类型"
|
||
# (astaux.vp 先于 ast.base.vp 编译时必现:AST.accept 仅有 vpi 注入条目)
|
||
GenApi.update_global_func_spec(lookup_name, func)
|
||
|
||
|
||
# ============================================================
|
||
# _register_ctypedef_from_annassign - 从 AnnAssign 节点注册 CTypedef
|
||
#
|
||
# 提取 NAME: t.CTypedef = Type 中的名称和类型表达式字符串,
|
||
# 调用 register_ctypedef 注册到动态 CTypedef 表。
|
||
# ============================================================
|
||
def _register_ctypedef_from_annassign(pool: memhub.MemBuddy*,
|
||
aa: ast.AnnAssign*) -> 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* = ast.Name*(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)
|
||
|
||
|
||
# ============================================================
|
||
# REPL:includes 类**完整布局**与虚表恢复(.stub.ll 驱动)
|
||
#
|
||
# 为什么需要(M4 遗留缺口):会话初始化用 .vpi 注入注册 includes 类,而 .vpi 只含
|
||
# "类名 + 本类 AnnAssign 字段 + 方法签名" ⇒ **父类字段**与 **__vtable__ 占位
|
||
# 字段**全部缺失 ⇒ 带父类/虚表的类在片段侧的字段索引与宿主真实布局错位
|
||
# (静默读错字段)。
|
||
#
|
||
# 布局真源 = .stub.ll(批处理落盘点:Phase1Main.vp:1078 / Phase2/__init__.vp:1050
|
||
# 用 tr.dump_ir(..., llvmlite.OUTPUT_STUB)):
|
||
# - `%"<sha1>.<Class>" = type { ... }` 是**展平后**的完整布局(ClassGenDef 的
|
||
# 字段展平:__vtable__ 首字段 + 父类字段 + 本类字段);
|
||
# - `@...<sha1>.<Class>.vtable` 全局的类型槽数 = 该类的虚方法数
|
||
# (ClassGenVTable._generate_vtable 按 _collect_vtable_methods 的顺序发射)。
|
||
#
|
||
# 本段仅在 ReplStubLayoutMode != 0(REPL 会话初始化)时生效,批处理永不置位:
|
||
# ① 在 _inject_class_struct_declare **之前**登记完整布局:字段名/类型与
|
||
# ClassGenDef 同源(父类字段来自已在会话登记的父条目=字段展平;本类字段
|
||
# 来自本 vpi 的 AnnAssign,走同一条 resolve_annotation_type);
|
||
# ② 布局由 .stub.ll 的**字段数**校验:Count(名单) 必须等于存根的字段数
|
||
# (有虚表时 +1 = __vtable__);不符即判"布局不可信";
|
||
# ③ 虚表:HasVTable 与虚方法**短名顺序**按 ClassGenDef._detect_vtable_status /
|
||
# ClassGenBase._should_method_be_virtual 的规则转写(父类虚方法在前、本类
|
||
# 合格方法按类体顺序追加、按短名去重),并用存根 .vtable 槽数校验;
|
||
# 槽数不符 ⇒ 不登记 VTableMethods(退化为直接调用 {sha1}.{Class}.{m})。
|
||
#
|
||
# 拒绝策略(能报错就别静默):布局不可信 ⇒ **不登记**该类的 StructEntry ⇒ 片段侧
|
||
# 字段访问报"类未注册/属性访问失败",绝不按错偏移静默访问;方法调用不受影响
|
||
# (self 只是指针,方法体在宿主里按真实布局编译,符号经宿主 import lib 解析)。
|
||
#
|
||
# 拒绝的两类成因必须分开(见 _REPL_SU_* 与 _repl_stub_warn_nodata):
|
||
# - 『数据缺失』= LayoutCount 只是哨兵(本模块没有 .stub.ll 文件 / 存根里查不到该
|
||
# 类),任一侧无数据 ⇒ **不比较**、也就不存在"字段数不符"这回事;文案与汇总计数
|
||
# 都写明成因,避免"-1 vs 推断 -1"这种两侧都无数据却像在比较的误导输出;
|
||
# - 『判据拒绝』= 数据在但不可信(字段数不符 / opaque / 父类被拒),一律不放行。
|
||
# 两者都**不登记**(M4.2 原则不变),差别只在"能不能靠补数据消除"。
|
||
#
|
||
# 逐模块隔离:全部工作发生在一次 .vpi 注入内(调用方已有 per-module _setjmp 兜底),
|
||
# 单模块失败只告警 + 该类不可用,不终止会话;配合 ReplInitIncludesClasses 的
|
||
# 多遍收敛,跨模块父类在后续遍就绪后即可登记(存根元数据已缓存在类表,无需重解析)。
|
||
# ============================================================
|
||
|
||
# 会话级状态(PermPool;会话初始化期间有效)
|
||
ReplStubLayoutMode: int = 0
|
||
ReplStubPool: memhub.MemBuddy* = None
|
||
ReplStubTempDir: str = None
|
||
ReplStubTable: sizet* = None
|
||
ReplStubCount: int = 0
|
||
ReplStubOk: int = 0
|
||
ReplStubVtOk: int = 0
|
||
ReplStubRefused: int = 0
|
||
ReplStubRefusedNoStub: int = 0 # 拒绝原因分布(_REPL_SU_*):数据缺失(模块无 .stub.ll)
|
||
ReplStubRefusedNoClass: int = 0 # 判据拒绝:存根有该模块但无此类
|
||
ReplStubRefusedOpaque: int = 0 # 判据拒绝:存根里该类 opaque
|
||
ReplStubRefusedParent: int = 0 # 上游无数据:父类不可用/未注册
|
||
ReplStubNoStub: int = 0
|
||
ReplStubWarnLeft: int = 0
|
||
ReplStubTmpCount: int = -1
|
||
ReplStubTmpPacked: int = 0
|
||
ReplStubTmpArity: int = 0
|
||
ReplStubNameBuf: str = None
|
||
|
||
|
||
def _repl_stub_dupstr(pool: memhub.MemBuddy*, src: str) -> str:
|
||
"""把字符串深拷贝到指定池"""
|
||
if src is None or pool is None:
|
||
return None
|
||
ln: sizet = string.strlen(src)
|
||
dst: str = pool.alloc(ln + 1)
|
||
if dst is None:
|
||
return None
|
||
string.strcpy(dst, src)
|
||
return dst
|
||
|
||
|
||
def _repl_stub_key(pool: memhub.MemBuddy*, sha1: str, cls: str) -> str:
|
||
"""拼接 <sha1>.<类名>:与定义侧 ClassGenDef 的命名结构体名逐字一致"""
|
||
if pool is None or sha1 is None or cls is None:
|
||
return None
|
||
if sha1[0] == '\0' or cls[0] == '\0':
|
||
return None
|
||
ln: sizet = string.strlen(sha1) + 1 + string.strlen(cls) + 1
|
||
buf: str = pool.alloc(ln)
|
||
if buf is None:
|
||
return None
|
||
viperlib.snprintf(buf, ln, "%s.%s", sha1, cls)
|
||
return buf
|
||
|
||
|
||
def _repl_stub_path(sha1: str, ext: str) -> str:
|
||
"""REPL 临时目录下的切片路径 <temp_dir>/<sub>/<sha1>.<ext>(stdlib 分配)"""
|
||
if ReplStubTempDir is None or sha1 is None:
|
||
return None
|
||
td_len: sizet = string.strlen(ReplStubTempDir)
|
||
sh_len: sizet = string.strlen(sha1)
|
||
sub: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
||
sub_len: sizet = 0
|
||
if sub is not None:
|
||
sub_len = string.strlen(sub)
|
||
cap: sizet = td_len + sub_len + sh_len + 8
|
||
buf: str = stdlib.malloc(cap)
|
||
if buf is None:
|
||
return None
|
||
if sub_len > 0:
|
||
viperlib.snprintf(buf, cap, "%s/%s/%s.%s", ReplStubTempDir, sub, sha1, ext)
|
||
else:
|
||
viperlib.snprintf(buf, cap, "%s/%s.%s", ReplStubTempDir, sha1, ext)
|
||
return buf
|
||
|
||
|
||
def _repl_stub_readtext(path: str) -> str:
|
||
"""读入存根文本(stdlib.malloc,调用方 free);不存在/超限返回 None"""
|
||
if path is None:
|
||
return None
|
||
f: rawptr[fileio.File] = fileio.File(path, fileio.MODE.R)
|
||
if f is None or f.closed:
|
||
return None
|
||
sz: sizet = f.size()
|
||
if sz <= 0 or sz >= _REPL_STUB_TXT_CAP:
|
||
f.close()
|
||
return None
|
||
buf: str = stdlib.malloc(sz + 1)
|
||
if buf is None:
|
||
f.close()
|
||
return None
|
||
rd: LONG = f.read_all(buf, sz + 1)
|
||
f.close()
|
||
if rd <= 0:
|
||
stdlib.free(buf)
|
||
return None
|
||
if rd < LONG(sz + 1):
|
||
buf[rd] = 0
|
||
return buf
|
||
|
||
|
||
# 从 txt[pos] 起取一个标识符(跳过 '%' / '@' 前缀与可选引号)写进 ReplStubNameBuf;
|
||
# 返回名字之后的位置(0 = 失败/超长;合法调用至少前进 2 字符,故 0 可作失败哨兵)
|
||
def _repl_stub_take_name(txt: str, pos: sizet) -> sizet:
|
||
global ReplStubNameBuf
|
||
if txt is None or ReplStubNameBuf is None:
|
||
return 0
|
||
n: sizet = string.strlen(txt)
|
||
i: sizet = pos
|
||
if i < n and (txt[i] == '%' or txt[i] == '@'):
|
||
i += 1
|
||
if i >= n:
|
||
return 0
|
||
if txt[i] == '\"':
|
||
i += 1
|
||
k: sizet = 0
|
||
while i < n and txt[i] != '\"':
|
||
if k + 1 >= _REPL_STUB_NAME_CAP:
|
||
return 0
|
||
ReplStubNameBuf[k] = txt[i]
|
||
k += 1
|
||
i += 1
|
||
if i >= n:
|
||
return 0
|
||
ReplStubNameBuf[k] = 0
|
||
return i + 1
|
||
k2: sizet = 0
|
||
while i < n:
|
||
ch: char = txt[i]
|
||
if ch == ' ' or ch == '\t' or ch == '=' or ch == ',' or ch == '{' or ch == '}':
|
||
break
|
||
if ch == '[' or ch == ']' or ch == '(' or ch == ')' or ch == '*' or ch == '<' or ch == '>':
|
||
break
|
||
if k2 + 1 >= _REPL_STUB_NAME_CAP:
|
||
return 0
|
||
ReplStubNameBuf[k2] = ch
|
||
k2 += 1
|
||
i += 1
|
||
if k2 == 0:
|
||
return 0
|
||
ReplStubNameBuf[k2] = 0
|
||
return i
|
||
|
||
|
||
# 从 txt[pos](应指向 '{')起数顶层字段数:{} / [] / () / <> / "..." 内的逗号不计;
|
||
# 返回字段数(-1 = 未闭合/格式异常)
|
||
def _repl_stub_body_count(txt: str, pos: sizet) -> int:
|
||
if txt is None:
|
||
return -1
|
||
n: sizet = string.strlen(txt)
|
||
i: sizet = pos
|
||
if i >= n or txt[i] != '{':
|
||
return -1
|
||
i += 1
|
||
depth: int = 0
|
||
in_str: int = 0
|
||
cnt: int = 0
|
||
has: int = 0
|
||
while i < n:
|
||
ch: char = txt[i]
|
||
if in_str != 0:
|
||
if ch == '\"':
|
||
in_str = 0
|
||
i += 1
|
||
continue
|
||
if ch == '\"':
|
||
in_str = 1
|
||
has = 1
|
||
i += 1
|
||
continue
|
||
if ch == 0:
|
||
return -1
|
||
if ch == ',':
|
||
if depth == 0:
|
||
cnt += 1
|
||
i += 1
|
||
continue
|
||
if ch == '{' or ch == '[' or ch == '(' or ch == '<':
|
||
depth += 1
|
||
has = 1
|
||
i += 1
|
||
continue
|
||
if ch == '}' or ch == ']' or ch == ')' or ch == '>':
|
||
if ch == '}' and depth == 0:
|
||
if has == 0:
|
||
return 0
|
||
return cnt + 1
|
||
depth -= 1
|
||
i += 1
|
||
continue
|
||
if ch != ' ' and ch != '\t' and ch != '\n' and ch != '\r':
|
||
has = 1
|
||
i += 1
|
||
return -1
|
||
|
||
|
||
# 扫描存根文本,取 `%"<key>" = type ...` 与 `@...<key>.vtable = ...` 两条元数据,
|
||
# 结果写全局 scratch:ReplStubTmpCount(字段数 / -1 无此类 / -2 opaque)、
|
||
# ReplStubTmpPacked、ReplStubTmpArity(.vtable 槽数,0 = 无该全局)
|
||
def _repl_stub_scan_meta(txt: str, key: str) -> None:
|
||
global ReplStubTmpCount, ReplStubTmpPacked, ReplStubTmpArity
|
||
ReplStubTmpCount = -1
|
||
ReplStubTmpPacked = 0
|
||
ReplStubTmpArity = 0
|
||
if txt is None or key is None:
|
||
return
|
||
n: sizet = string.strlen(txt)
|
||
key_len: sizet = string.strlen(key)
|
||
i: sizet = 0
|
||
while i < n:
|
||
ls: sizet = i
|
||
while i < n and txt[i] != '\n':
|
||
i += 1
|
||
le: sizet = i
|
||
if i < n:
|
||
i += 1
|
||
p: sizet = ls
|
||
while p < le and (txt[p] == ' ' or txt[p] == '\t' or txt[p] == '\r'):
|
||
p += 1
|
||
if p >= le:
|
||
continue
|
||
c0: char = txt[p]
|
||
if c0 == '%':
|
||
# 类型定义行
|
||
q: sizet = _repl_stub_take_name(txt, p)
|
||
if q == 0 or q > le:
|
||
continue
|
||
if string.strcmp(ReplStubNameBuf, key) != 0:
|
||
continue
|
||
# 找 "= type"
|
||
j: sizet = q
|
||
while j + 6 < le and string.strncmp(txt + j, "= type", 6) != 0:
|
||
j += 1
|
||
if j + 6 >= le:
|
||
continue
|
||
j += 6
|
||
while j < le and (txt[j] == ' ' or txt[j] == '\t'):
|
||
j += 1
|
||
if j + 6 <= le and string.strncmp(txt + j, "opaque", 6) == 0:
|
||
ReplStubTmpCount = -2
|
||
continue
|
||
if j < le and txt[j] == '<':
|
||
ReplStubTmpPacked = 1
|
||
j += 1
|
||
if j < le and txt[j] == '{':
|
||
ReplStubTmpCount = _repl_stub_body_count(txt, j)
|
||
continue
|
||
if c0 == '@':
|
||
# 全局变量行:只关心 <key>.vtable
|
||
q2: sizet = _repl_stub_take_name(txt, p)
|
||
if q2 == 0 or q2 > le:
|
||
continue
|
||
nm_len: sizet = string.strlen(ReplStubNameBuf)
|
||
if nm_len != key_len + 7:
|
||
continue
|
||
if string.strncmp(ReplStubNameBuf, key, key_len) != 0:
|
||
continue
|
||
if string.strncmp(ReplStubNameBuf + key_len, ".vtable", 7) != 0:
|
||
continue
|
||
j2: sizet = q2
|
||
while j2 < le and txt[j2] != '=':
|
||
j2 += 1
|
||
if j2 >= le:
|
||
continue
|
||
j2 += 1
|
||
while j2 < le and txt[j2] != '{':
|
||
j2 += 1
|
||
if j2 < le and txt[j2] == '{':
|
||
ar: int = _repl_stub_body_count(txt, j2)
|
||
if ar > 0:
|
||
ReplStubTmpArity = ar
|
||
continue
|
||
return
|
||
|
||
|
||
def ReplStubLayoutBegin(temp_dir: str) -> int:
|
||
"""开启会话级"存根布局恢复"(Repl.vp 在 includes 类注册前调用一次);0=成功"""
|
||
global ReplStubLayoutMode, ReplStubPool, ReplStubTempDir, ReplStubTable
|
||
global ReplStubCount, ReplStubOk, ReplStubVtOk, ReplStubRefused, ReplStubNoStub
|
||
global ReplStubWarnLeft, ReplStubNameBuf
|
||
global ReplStubRefusedNoStub, ReplStubRefusedNoClass, ReplStubRefusedOpaque, ReplStubRefusedParent
|
||
pool: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||
if pool is None or temp_dir is None:
|
||
return 1
|
||
ReplStubPool = pool
|
||
ReplStubTempDir = _repl_stub_dupstr(pool, temp_dir)
|
||
if ReplStubTempDir is None:
|
||
return 1
|
||
ReplStubTable = pool.alloc(8 * _REPL_STUB_CLS_MAX)
|
||
if ReplStubTable is None:
|
||
return 1
|
||
string.memset(ReplStubTable, 0, 8 * _REPL_STUB_CLS_MAX)
|
||
ReplStubNameBuf = pool.alloc(_REPL_STUB_NAME_CAP)
|
||
if ReplStubNameBuf is None:
|
||
return 1
|
||
ReplStubNameBuf[0] = 0
|
||
ReplStubCount = 0
|
||
ReplStubOk = 0
|
||
ReplStubVtOk = 0
|
||
ReplStubRefused = 0
|
||
ReplStubRefusedNoStub = 0
|
||
ReplStubRefusedNoClass = 0
|
||
ReplStubRefusedOpaque = 0
|
||
ReplStubRefusedParent = 0
|
||
ReplStubNoStub = 0
|
||
ReplStubWarnLeft = _REPL_STUB_WARN_MAX
|
||
ReplStubLayoutMode = 1
|
||
return 0
|
||
|
||
|
||
def _repl_stub_at(i: int) -> rawptr[ReplStubClass]:
|
||
"""类表第 i 项(无则 None)"""
|
||
if ReplStubTable is None or i < 0 or i >= ReplStubCount:
|
||
return None
|
||
addr: sizet = ReplStubTable[i]
|
||
if addr == 0:
|
||
return None
|
||
return rawptr[ReplStubClass](uint64(addr))
|
||
|
||
|
||
def _repl_stub_find(key: str) -> rawptr[ReplStubClass]:
|
||
"""按 "<sha1>.<类名>" 查类表条目(无则 None)"""
|
||
if key is None:
|
||
return None
|
||
i: int
|
||
for i in range(ReplStubCount):
|
||
e: rawptr[ReplStubClass] = _repl_stub_at(i)
|
||
if e is None or e.Key is None:
|
||
continue
|
||
if string.strcmp(e.Key, key) == 0:
|
||
return e
|
||
return None
|
||
|
||
|
||
def _repl_stub_find_base(e: rawptr[ReplStubClass], bi: int) -> rawptr[ReplStubClass]:
|
||
"""父类表条目:先同模块 SHA1 精确匹配,再按裸类名首个匹配(镜像
|
||
StructGen.find_struct_by_module → find_struct 的父类回退口径)"""
|
||
if e is None or e.Bases is None or bi < 0 or bi >= e.BaseCount:
|
||
return None
|
||
pname: str = rawptr(e.Bases[bi])
|
||
if pname is None:
|
||
return None
|
||
i: int
|
||
for i in range(ReplStubCount):
|
||
pe: rawptr[ReplStubClass] = _repl_stub_at(i)
|
||
if pe is None or pe.ClassName is None:
|
||
continue
|
||
if string.strcmp(pe.ClassName, pname) != 0:
|
||
continue
|
||
if e.Sha1 is not None and pe.Sha1 is not None:
|
||
if string.strcmp(pe.Sha1, e.Sha1) == 0:
|
||
return pe
|
||
for i in range(ReplStubCount):
|
||
pe2: rawptr[ReplStubClass] = _repl_stub_at(i)
|
||
if pe2 is None or pe2.ClassName is None:
|
||
continue
|
||
if string.strcmp(pe2.ClassName, pname) == 0:
|
||
return pe2
|
||
return None
|
||
|
||
|
||
def _repl_stub_has_name(arr: sizet*, cnt: int, name: str) -> int:
|
||
"""名单里是否已有该名(先声明优先去重)"""
|
||
if arr is None or name is None:
|
||
return 0
|
||
i: int
|
||
for i in range(cnt):
|
||
addr: sizet = arr[i]
|
||
if addr == 0:
|
||
continue
|
||
ex: str = rawptr(addr)
|
||
if ex is not None and string.strcmp(ex, name) == 0:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _repl_stub_deco_flags(deco_list: list[ast.AST*]*) -> int:
|
||
"""类/方法装饰器标志(位0=@t.NoVTable,位1=@t.CVTable)
|
||
|
||
与 ClassGenBase._has_decorator 同判据(Attribute(t.NoVTable) 与 Name(NoVTable) 两种写法)。
|
||
"""
|
||
if deco_list is None:
|
||
return 0
|
||
flags: int = 0
|
||
dn: sizet = len(deco_list)
|
||
di: sizet
|
||
for di in range(dn):
|
||
deco: ast.AST* = deco_list.get(di)
|
||
if deco is None:
|
||
continue
|
||
nm: str = None
|
||
k: int = deco.kind()
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(deco)
|
||
if at.attr is not None and at.value is not None \
|
||
and at.value.kind() == ast.ASTKind.Name:
|
||
vn: ast.Name* = ast.Name*(at.value)
|
||
if vn.id is not None and string.strcmp(vn.id, "t") == 0:
|
||
nm = at.attr
|
||
elif k == ast.ASTKind.Name:
|
||
nn: ast.Name* = ast.Name*(deco)
|
||
nm = nn.id
|
||
if nm is None:
|
||
continue
|
||
if string.strcmp(nm, "NoVTable") == 0:
|
||
flags = flags | 1
|
||
elif string.strcmp(nm, "CVTable") == 0:
|
||
flags = flags | 2
|
||
return flags
|
||
|
||
|
||
def _repl_stub_is_virtual(e: rawptr[ReplStubClass], mi: int,
|
||
first: rawptr[ReplStubClass]) -> int:
|
||
"""本类第 mi 个方法是否进虚表(转写 ClassGenBase._should_method_be_virtual)"""
|
||
if e is None or e.MethodFlags is None or mi < 0 or mi >= e.MethodCount:
|
||
return 0
|
||
mf: int = int(e.MethodFlags[mi])
|
||
if (mf & 1) != 0:
|
||
return 0
|
||
if (mf & 2) != 0:
|
||
return 1
|
||
if (e.Flags & _REPL_SF_CVTABLE) != 0:
|
||
return 1
|
||
if first is not None:
|
||
if first.IsNoVTable != 0:
|
||
return 0
|
||
return first.HasVt
|
||
return 0
|
||
|
||
|
||
def _repl_stub_warn_refuse(e: rawptr[ReplStubClass], why: str, a: int, b: int) -> None:
|
||
"""**判据拒绝**告警(两侧都是真数字时才用它):存根字段数 a 与展平+虚表推断 b 不符"""
|
||
global ReplStubWarnLeft, ReplStubRefused
|
||
ReplStubRefused += 1
|
||
if ReplStubWarnLeft <= 0:
|
||
return
|
||
ReplStubWarnLeft -= 1
|
||
stdio.printf("includes 类布局: %s.%s 布局不可信 ⇒ 明确拒绝(原因=判据拒绝:%s;"
|
||
"存根字段数=%d ≠ 展平字段数+虚表位=%d)。该类字段访问被明确拒绝"
|
||
"(方法调用不受影响)\n",
|
||
e.Sha1, e.ClassName, why, a, b)
|
||
|
||
|
||
def _repl_stub_warn_nodata(e: rawptr[ReplStubClass], cause: int) -> None:
|
||
"""『无数据 ⇒ 明确拒绝』告警(与 _repl_stub_warn_refuse 分开)
|
||
|
||
与"字段数不符"不同,这条路径上根本没做比较(任一侧无数据即不比较,也不假装成
|
||
"不匹配"),所以文案打印两侧数据的来源而不是数字,并把『数据缺失』
|
||
(补存根/重跑即可消除)与『判据拒绝』(数据在但不可信,绝不放行)分开。
|
||
"""
|
||
global ReplStubWarnLeft, ReplStubRefused
|
||
global ReplStubRefusedNoStub, ReplStubRefusedNoClass
|
||
global ReplStubRefusedOpaque, ReplStubRefusedParent
|
||
ReplStubRefused += 1
|
||
if cause == _REPL_SU_NOSTUB:
|
||
ReplStubRefusedNoStub += 1
|
||
elif cause == _REPL_SU_NOCLASS:
|
||
ReplStubRefusedNoClass += 1
|
||
elif cause == _REPL_SU_OPAQUE:
|
||
ReplStubRefusedOpaque += 1
|
||
else:
|
||
ReplStubRefusedParent += 1
|
||
if ReplStubWarnLeft <= 0:
|
||
return
|
||
ReplStubWarnLeft -= 1
|
||
if cause == _REPL_SU_NOSTUB:
|
||
# 数据缺失:连模块存根文件都没有(LayoutCount = -1 只是"没读到存根"的哨兵)
|
||
stdio.printf("includes 类布局: %s.%s 布局不可用 ⇒ 明确拒绝(原因=『数据缺失』:"
|
||
"模块 %s 没有 .stub.ll 文件;存根字段数=无 / 推断字段数=未计算 ⇒ "
|
||
"两侧都没数据,『未做比较』,不是字段数不符)。该类字段访问被"
|
||
"拒绝(方法调用不受影响);补上该模块存根后用同一 --project 重跑 "
|
||
"REPL 即恢复\n", e.Sha1, e.ClassName, e.Sha1)
|
||
elif cause == _REPL_SU_OPAQUE:
|
||
# 判据拒绝:存根里该 key 是 opaque(无 body ⇒ 无字段数可比)
|
||
stdio.printf("includes 类布局: %s.%s 布局不可用 ⇒ 明确拒绝(原因=『判据拒绝』:"
|
||
"存根 %s.stub.ll 里键 %s 为 opaque 声明(无 body);存根字段数=不可得 / "
|
||
"推断字段数=未计算 ⇒ 『未做比较』)。该类字段访问被拒绝"
|
||
"(方法调用不受影响)\n", e.Sha1, e.ClassName, e.Sha1, e.Key)
|
||
else:
|
||
# 判据拒绝:存根文件在,但没有该类的 `= type` 行(LayoutCount = -1 只是"未找到"的哨兵)
|
||
stdio.printf("includes 类布局: %s.%s 布局不可用 ⇒ 明确拒绝(原因=『判据拒绝』:"
|
||
"存根 %s.stub.ll 存在但其中没有键 %s 的 `= type {…}` 定义行;"
|
||
"存根字段数=未找到 / 推断字段数=未计算 ⇒ 『未做比较』)。该类字段访问"
|
||
"被拒绝(方法调用不受影响);若该类确实定义于该模块,说明存根陈旧/"
|
||
"不完整,请重建\n", e.Sha1, e.ClassName, e.Sha1, e.Key)
|
||
|
||
|
||
def _repl_stub_warn_parent(e: rawptr[ReplStubClass], detail: str, pname: str) -> None:
|
||
"""**上游无数据 ⇒ 明确拒绝**:父类布局不可用/未注册(本类无法展平 ⇒ 不比较)"""
|
||
global ReplStubWarnLeft, ReplStubRefused, ReplStubRefusedParent
|
||
ReplStubRefused += 1
|
||
ReplStubRefusedParent += 1
|
||
if ReplStubWarnLeft <= 0:
|
||
return
|
||
ReplStubWarnLeft -= 1
|
||
stdio.printf("includes 类布局: %s.%s 布局不可用 ⇒ 明确拒绝(原因=『上游无数据』:%s,"
|
||
"父类=%s;父类字段与虚表位无法展平 ⇒ 本类未做字段数比较)。该类字段访问"
|
||
"被拒绝(方法调用不受影响)\n", e.Sha1, e.ClassName, detail, pname)
|
||
|
||
|
||
def _repl_stub_try(e: rawptr[ReplStubClass], mod: llvmlite.LLVMModule*) -> int:
|
||
"""按存根校验并登记完整布局:1=已登记 / 0=父类未就绪(推迟)/ -1=拒绝"""
|
||
global ReplStubOk, ReplStubVtOk, ReplStubWarnLeft
|
||
if e is None:
|
||
return -1
|
||
if e.State == _REPL_SS_OK:
|
||
return 1
|
||
if e.State == _REPL_SS_REFUSED:
|
||
return -1
|
||
if e.State != _REPL_SS_PENDING:
|
||
return -1
|
||
pool: memhub.MemBuddy* = ReplStubPool
|
||
if pool is None:
|
||
e.State = _REPL_SS_REFUSED
|
||
return -1
|
||
# ① 父类必须已就绪(未就绪 ⇒ 推迟;被拒 ⇒ 本类布局不可信)
|
||
bi: int
|
||
for bi in range(e.BaseCount):
|
||
pe: rawptr[ReplStubClass] = _repl_stub_find_base(e, bi)
|
||
if pe is None:
|
||
return 0
|
||
if pe.State == _REPL_SS_PENDING:
|
||
return 0
|
||
if pe.State == _REPL_SS_REFUSED:
|
||
e.State = _REPL_SS_REFUSED
|
||
_repl_stub_warn_parent(e, "父类布局不可信", pe.ClassName)
|
||
return -1
|
||
if pe.State == _REPL_SS_EMPTY and pe.OwnFail != 0:
|
||
# 父类自身有字段注解解析失败 ⇒ 其 Count 只是"已解析字段数"(不是完整布局)
|
||
# ⇒ 展平出来的本类字段数不可用:既不比较(避免误报"字段数不符")、也不放行
|
||
# (M4.2:布局不可信 ⇒ 明确拒绝)。
|
||
e.State = _REPL_SS_REFUSED
|
||
_repl_stub_warn_parent(e, "父类的字段类型未全部解析(其字段数不完整)",
|
||
pe.ClassName)
|
||
return -1
|
||
first: rawptr[ReplStubClass] = None
|
||
if e.BaseCount > 0:
|
||
first = _repl_stub_find_base(e, 0)
|
||
# ② HasVTable(转写 ClassGenBase._detect_vtable_status)
|
||
hv: int = 0
|
||
if (e.Flags & _REPL_SF_CVTABLE) != 0:
|
||
hv = 1
|
||
elif (e.Flags & _REPL_SF_NOVTABLE) != 0:
|
||
hv = 1 if (e.Flags & _REPL_SF_ANYFUNC) != 0 else 0
|
||
elif first is not None:
|
||
if first.IsNoVTable != 0:
|
||
hv = 1 if (e.Flags & _REPL_SF_ANYFUNC) != 0 else 0
|
||
else:
|
||
hv = first.HasVt
|
||
else:
|
||
hv = 1 if (e.Flags & _REPL_SF_ANYFUNC) != 0 else 0
|
||
# ③ 字段展平(父类字段在前 + 本类字段,短名去重先声明优先)
|
||
cn: int = 0
|
||
for bi in range(e.BaseCount):
|
||
pe: rawptr[ReplStubClass] = _repl_stub_find_base(e, bi)
|
||
fi: int
|
||
for fi in range(pe.Count):
|
||
if cn >= _REPL_STUB_FIELD_MAX:
|
||
break
|
||
nm: str = rawptr(pe.Names[fi])
|
||
if nm is None:
|
||
continue
|
||
if _repl_stub_has_name(e.Names, cn, nm) != 0:
|
||
continue
|
||
e.Names[cn] = pe.Names[fi]
|
||
e.Types[cn] = pe.Types[fi]
|
||
cn += 1
|
||
oi: int
|
||
for oi in range(e.OwnCount):
|
||
if cn >= _REPL_STUB_FIELD_MAX:
|
||
break
|
||
nm2: str = rawptr(e.OwnNames[oi])
|
||
if nm2 is None:
|
||
continue
|
||
if _repl_stub_has_name(e.Names, cn, nm2) != 0:
|
||
continue
|
||
e.Names[cn] = e.OwnNames[oi]
|
||
e.Types[cn] = e.OwnTypes[oi]
|
||
cn += 1
|
||
e.Count = cn
|
||
e.HasVt = hv
|
||
# ④ 布局校验(**仅当两侧都可用时才走到这里**:LayoutCount >= 0 已由 PENDING 前提
|
||
# 保证,展平侧由 ① 保证父类字段数完整):存根字段数 == __vtable__(有虚表时) + 展平字段数
|
||
if e.LayoutCount != cn + hv:
|
||
e.State = _REPL_SS_REFUSED
|
||
_repl_stub_warn_refuse(e, "存根字段数与字段名单不符", e.LayoutCount, cn + hv)
|
||
return -1
|
||
# ⑤ 虚方法短名顺序(父类虚方法在前 + 本类合格方法按类体顺序)
|
||
vn: int = 0
|
||
if hv == 1:
|
||
for bi in range(e.BaseCount):
|
||
pe: rawptr[ReplStubClass] = _repl_stub_find_base(e, bi)
|
||
vi: int
|
||
for vi in range(pe.VtCount):
|
||
if vn >= _REPL_STUB_VT_MAX:
|
||
break
|
||
mn: str = rawptr(pe.VtNames[vi])
|
||
if mn is None:
|
||
continue
|
||
if _repl_stub_has_name(e.VtNames, vn, mn) != 0:
|
||
continue
|
||
e.VtNames[vn] = pe.VtNames[vi]
|
||
vn += 1
|
||
mi: int
|
||
for mi in range(e.MethodCount):
|
||
if vn >= _REPL_STUB_VT_MAX:
|
||
break
|
||
mn2: str = rawptr(e.MethodNames[mi])
|
||
if mn2 is None:
|
||
continue
|
||
if string.strcmp(mn2, "__init__") == 0:
|
||
continue
|
||
if string.strcmp(mn2, "__before_init__") == 0:
|
||
continue
|
||
if string.strcmp(mn2, "__new__") == 0:
|
||
continue
|
||
if _repl_stub_has_name(e.VtNames, vn, mn2) != 0:
|
||
continue
|
||
if _repl_stub_is_virtual(e, mi, first) == 0:
|
||
continue
|
||
e.VtNames[vn] = e.MethodNames[mi]
|
||
vn += 1
|
||
e.VtCount = vn
|
||
e.VtTrust = 0
|
||
if hv == 1 and vn > 0:
|
||
if vn == e.VtArity:
|
||
e.VtTrust = 1
|
||
elif ReplStubWarnLeft > 0:
|
||
ReplStubWarnLeft -= 1
|
||
stdio.printf("includes 类布局: %s.%s 的虚表槽数与存根不符(%d vs %d)"
|
||
"——该类不做虚分派(退化为直接调用,会话继续)\n",
|
||
e.Sha1, e.ClassName, vn, e.VtArity)
|
||
# ⑥ 建 LLVM 结构体(命名 "%<sha1>.<类名>",与定义侧同口径)
|
||
total: int = cn + hv
|
||
i8_ptr: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||
if i8_ptr is None:
|
||
return 0
|
||
first_node: rawptr[llvmlite.ParamNode] = None
|
||
prev_node: rawptr[llvmlite.ParamNode] = None
|
||
if hv == 1:
|
||
nd0: rawptr[llvmlite.ParamNode] = pool.alloc(llvmlite.ParamNode.__sizeof__())
|
||
if nd0 is None:
|
||
return 0
|
||
string.memset(nd0, 0, llvmlite.ParamNode.__sizeof__())
|
||
nd0.Ty = i8_ptr
|
||
first_node = nd0
|
||
prev_node = nd0
|
||
fi2: int
|
||
for fi2 in range(cn):
|
||
fty: llvmlite.LLVMType* = (llvmlite.LLVMType*)(e.Types[fi2])
|
||
if fty is None:
|
||
continue
|
||
nd: rawptr[llvmlite.ParamNode] = pool.alloc(llvmlite.ParamNode.__sizeof__())
|
||
if nd is None:
|
||
continue
|
||
string.memset(nd, 0, llvmlite.ParamNode.__sizeof__())
|
||
nd.Ty = fty
|
||
if first_node is None:
|
||
first_node = nd
|
||
if prev_node is not None:
|
||
prev_node.Next = nd
|
||
prev_node = nd
|
||
struct_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, first_node, total, e.Key)
|
||
if struct_ty is None:
|
||
return 0
|
||
# ⑦ 登记(StdlibAlloc=1 期间的注册走 PermPool,跨片段存活)
|
||
entry: GenTypes.StructEntry* = GenApi.register_struct(
|
||
pool, e.ClassName, struct_ty, e.Sha1)
|
||
if entry is None:
|
||
e.State = _REPL_SS_REFUSED
|
||
return -1
|
||
if mod is not None:
|
||
llvmlite.module_add_named_type(mod, pool, struct_ty)
|
||
if (e.Flags & _REPL_SF_PACKED) != 0:
|
||
entry.IsPacked = 1
|
||
if mod is not None:
|
||
llvmlite.module_set_named_type_packed(mod, pool, struct_ty, 1)
|
||
if hv == 1:
|
||
entry.HasVTable = 1
|
||
GenApi.add_field(pool, entry, "__vtable__", i8_ptr)
|
||
fi3: int
|
||
for fi3 in range(cn):
|
||
fname: str = rawptr(e.Names[fi3])
|
||
fty3: llvmlite.LLVMType* = (llvmlite.LLVMType*)(e.Types[fi3])
|
||
if fname is None or fty3 is None:
|
||
continue
|
||
GenApi.add_field(pool, entry, fname, fty3)
|
||
if e.IsNoVTable != 0:
|
||
entry.IsNoVTable = 1
|
||
entry.IsOOP = e.IsOOP
|
||
entry.HasInit = e.HasInit
|
||
entry.HasNew = e.HasNew
|
||
# 父类名(多继承数组;顺序同声明序,供 vtable/MRO/字段继承查询)
|
||
if e.BaseCount > 0:
|
||
arr: sizet* = pool.alloc(8 * e.BaseCount)
|
||
if arr is not None:
|
||
string.memset(arr, 0, 8 * e.BaseCount)
|
||
bi3: int
|
||
for bi3 in range(e.BaseCount):
|
||
bnm: str = rawptr(e.Bases[bi3])
|
||
bdup: str = _repl_stub_dupstr(pool, bnm)
|
||
if bdup is not None:
|
||
arr[bi3] = sizet(bdup)
|
||
entry.ParentNames = arr
|
||
entry.ParentCount = e.BaseCount
|
||
pn: str = rawptr(e.Bases[0])
|
||
entry.ParentName = _repl_stub_dupstr(pool, pn)
|
||
if hv == 1 and e.VtTrust == 1 and e.VtCount > 0:
|
||
entry.VTableMethods = e.VtNames
|
||
entry.VTableMethodCount = e.VtCount
|
||
ReplStubVtOk += 1
|
||
e.State = _REPL_SS_OK
|
||
ReplStubOk += 1
|
||
return 1
|
||
|
||
|
||
def _repl_stub_add_class(pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
cd: ast.ClassDef*,
|
||
sha1: str,
|
||
txt: str,
|
||
have_stub: int,
|
||
trans: HT.Translator*,
|
||
imported_modules: str,
|
||
from_imports: str) -> int:
|
||
"""收集单个 vpi ClassDef 的布局要素并尝试登记(1=已登记/推迟处理,0=跳过)
|
||
|
||
have_stub:本模块是否读到了 .stub.ll 文本(0 时本模块带父类/虚表的类按
|
||
『数据缺失』拒绝,与"存根在但无此类"的『判据拒绝』在文案/计数上分开)。
|
||
"""
|
||
global ReplStubCount
|
||
if pool is None or cd is None or cd.name is None or ReplStubTable is None:
|
||
return 0
|
||
if cd.name[0] == '\0' or ReplStubCount >= _REPL_STUB_CLS_MAX:
|
||
return 0
|
||
key: str = _repl_stub_key(pool, sha1, cd.name)
|
||
if key is None:
|
||
return 0
|
||
if _repl_stub_find(key) is not None:
|
||
return 0
|
||
e: rawptr[ReplStubClass] = pool.alloc(ReplStubClass.__sizeof__())
|
||
if e is None:
|
||
return 0
|
||
string.memset(e, 0, ReplStubClass.__sizeof__())
|
||
e.Key = key
|
||
e.ClassName = _repl_stub_dupstr(pool, cd.name)
|
||
e.Sha1 = _repl_stub_dupstr(pool, sha1)
|
||
e.OwnNames = pool.alloc(8 * _REPL_STUB_FIELD_MAX)
|
||
e.OwnTypes = pool.alloc(8 * _REPL_STUB_FIELD_MAX)
|
||
e.MethodNames = pool.alloc(8 * _REPL_STUB_FIELD_MAX)
|
||
e.MethodFlags = pool.alloc(8 * _REPL_STUB_FIELD_MAX)
|
||
e.Bases = pool.alloc(8 * _REPL_STUB_FIELD_MAX)
|
||
e.Names = pool.alloc(8 * _REPL_STUB_FIELD_MAX)
|
||
e.Types = pool.alloc(8 * _REPL_STUB_FIELD_MAX)
|
||
e.VtNames = pool.alloc(8 * _REPL_STUB_VT_MAX)
|
||
if e.OwnNames is None or e.OwnTypes is None or e.MethodNames is None \
|
||
or e.MethodFlags is None or e.Bases is None or e.Names is None \
|
||
or e.Types is None or e.VtNames is None:
|
||
return 0
|
||
string.memset(e.OwnNames, 0, 8 * _REPL_STUB_FIELD_MAX)
|
||
string.memset(e.OwnTypes, 0, 8 * _REPL_STUB_FIELD_MAX)
|
||
string.memset(e.MethodNames, 0, 8 * _REPL_STUB_FIELD_MAX)
|
||
string.memset(e.MethodFlags, 0, 8 * _REPL_STUB_FIELD_MAX)
|
||
string.memset(e.Bases, 0, 8 * _REPL_STUB_FIELD_MAX)
|
||
string.memset(e.Names, 0, 8 * _REPL_STUB_FIELD_MAX)
|
||
string.memset(e.Types, 0, 8 * _REPL_STUB_FIELD_MAX)
|
||
string.memset(e.VtNames, 0, 8 * _REPL_STUB_VT_MAX)
|
||
# 类装饰器
|
||
cf: int = _repl_stub_deco_flags(cd.decorator_list)
|
||
if (cf & 2) != 0:
|
||
e.Flags = e.Flags | _REPL_SF_CVTABLE
|
||
if (cf & 1) != 0:
|
||
e.Flags = e.Flags | _REPL_SF_NOVTABLE
|
||
e.IsNoVTable = 1
|
||
if _vpi_class_has_packed(cd.decorator_list) != 0:
|
||
e.Flags = e.Flags | _REPL_SF_PACKED
|
||
# 真实父类(复用 ClassGenBase._collect_parents:别名解析 + marker 剔除)
|
||
pb: sizet* = pool.alloc(8 * GenTypes.PARENT_MAX)
|
||
bcnt: int = 0
|
||
if pb is not None and GenApi._g_gen is not None:
|
||
string.memset(pb, 0, 8 * GenTypes.PARENT_MAX)
|
||
bcnt = GenApi._g_gen.ClassGenBase._collect_parents(
|
||
pool, cd, pb, GenTypes.PARENT_MAX, trans)
|
||
if bcnt > 0:
|
||
if bcnt > _REPL_STUB_FIELD_MAX:
|
||
bcnt = _REPL_STUB_FIELD_MAX
|
||
bi: int
|
||
for bi in range(bcnt):
|
||
bnm: str = rawptr(pb[bi])
|
||
bdup: str = _repl_stub_dupstr(pool, bnm)
|
||
if bdup is not None:
|
||
e.Bases[bi] = sizet(bdup)
|
||
e.BaseCount = bcnt
|
||
# 类体扫描:字段(AnnAssign)+ 方法(FunctionDef)
|
||
children: list[ast.AST*]* = cd.children
|
||
own_fail: int = 0
|
||
if children is not None:
|
||
cn: sizet = len(children)
|
||
ci: sizet
|
||
for ci in range(cn):
|
||
ch2: ast.AST* = children.get(ci)
|
||
if ch2 is None:
|
||
continue
|
||
k2: int = ch2.kind()
|
||
if k2 == ast.ASTKind.AnnAssign:
|
||
if e.OwnCount >= _REPL_STUB_FIELD_MAX:
|
||
continue
|
||
aa2: ast.AnnAssign* = ast.AnnAssign*(ch2)
|
||
if aa2 is None or aa2.target is None or aa2.annotation is None:
|
||
continue
|
||
if aa2.target.kind() != ast.ASTKind.Name:
|
||
continue
|
||
tg: ast.Name* = ast.Name*(aa2.target)
|
||
if tg.id is None or tg.id[0] == '\0':
|
||
continue
|
||
fty2: llvmlite.LLVMType* = GenApi.resolve_annotation_type(
|
||
pool, aa2.annotation, imported_modules, from_imports, trans, 0)
|
||
if fty2 is None:
|
||
own_fail = 1
|
||
continue
|
||
fdup: str = _repl_stub_dupstr(pool, tg.id)
|
||
if fdup is None:
|
||
own_fail = 1
|
||
continue
|
||
e.OwnNames[e.OwnCount] = sizet(fdup)
|
||
e.OwnTypes[e.OwnCount] = sizet(fty2)
|
||
e.OwnCount += 1
|
||
continue
|
||
if k2 == ast.ASTKind.FunctionDef:
|
||
fd2: ast.FunctionDef* = ast.FunctionDef*(ch2)
|
||
if fd2 is None or fd2.name is None or fd2.name[0] == '\0':
|
||
continue
|
||
mf2: int = _repl_stub_deco_flags(fd2.decorator_list)
|
||
if (mf2 & 2) != 0:
|
||
e.Flags = e.Flags | _REPL_SF_ANYFUNC
|
||
if string.strcmp(fd2.name, "__init__") == 0:
|
||
e.HasInit = 1
|
||
elif string.strcmp(fd2.name, "__new__") == 0:
|
||
e.HasNew = 1
|
||
e.IsOOP = 1
|
||
if e.MethodCount < _REPL_STUB_FIELD_MAX:
|
||
e.MethodNames[e.MethodCount] = sizet(_repl_stub_dupstr(pool, fd2.name))
|
||
e.MethodFlags[e.MethodCount] = sizet(mf2)
|
||
e.MethodCount += 1
|
||
continue
|
||
# 存根元数据(缺存根时 LayoutCount 保持"未找到"哨兵;两种成因由 have_stub 区分)
|
||
e.LayoutCount = _REPL_STUB_NOCOUNT_NOCLASS
|
||
if txt is not None:
|
||
_repl_stub_scan_meta(txt, key)
|
||
e.LayoutCount = ReplStubTmpCount
|
||
e.VtArity = ReplStubTmpArity
|
||
if ReplStubTmpPacked != 0:
|
||
e.Flags = e.Flags | _REPL_SF_PACKED
|
||
# 状态判定
|
||
if own_fail != 0:
|
||
# 字段类型解析失败:交既有 vpi 注入口径(其整类跳过),本表只做展平来源
|
||
# (OwnFail 记下来:本类 OwnCount 只是"已解析字段数",不可当作完整布局供子类展平)
|
||
e.OwnFail = 1
|
||
e.State = _REPL_SS_EMPTY
|
||
e.Count = e.OwnCount
|
||
e.Names = e.OwnNames
|
||
e.Types = e.OwnTypes
|
||
elif e.LayoutCount >= 0:
|
||
e.State = _REPL_SS_PENDING
|
||
elif e.BaseCount == 0 and (e.Flags & (_REPL_SF_CVTABLE | _REPL_SF_ANYFUNC)) == 0:
|
||
# 无父类、无虚表 ⇒ 本类自有字段即完整布局(与既有口径等价)
|
||
e.State = _REPL_SS_EMPTY
|
||
e.Count = e.OwnCount
|
||
e.Names = e.OwnNames
|
||
e.Types = e.OwnTypes
|
||
else:
|
||
# 布局无数据 ⇒ 明确拒绝(不登记 StructEntry)。两类成因必须分开报:
|
||
# ① have_stub == 0:连模块 .stub.ll 都没有(**数据缺失**,补存根即可恢复)
|
||
# ② LayoutCount == -2:存根里该类是 opaque(判据拒绝)
|
||
# ③ 其余:存根在但没有该类的 `= type` 行(判据拒绝;若该类确实定义于此模块
|
||
# 则说明存根陈旧/不完整)
|
||
e.State = _REPL_SS_REFUSED
|
||
if have_stub == 0:
|
||
_repl_stub_warn_nodata(e, _REPL_SU_NOSTUB)
|
||
elif e.LayoutCount == _REPL_STUB_NOCOUNT_OPAQUE:
|
||
_repl_stub_warn_nodata(e, _REPL_SU_OPAQUE)
|
||
else:
|
||
_repl_stub_warn_nodata(e, _REPL_SU_NOCLASS)
|
||
ReplStubTable[ReplStubCount] = sizet(e)
|
||
ReplStubCount += 1
|
||
if e.State == _REPL_SS_PENDING:
|
||
_repl_stub_try(e, mod)
|
||
return 1
|
||
|
||
|
||
def _repl_stub_collect_module(pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
ch: list[ast.AST*]*,
|
||
n: sizet,
|
||
trans: HT.Translator*,
|
||
src_sha1: str,
|
||
imported_modules: str,
|
||
from_imports: str) -> int:
|
||
"""(REPL)为当前模块的类收集布局要素 + 尝试登记;返回处理的类数"""
|
||
global ReplStubNoStub, ReplStubWarnLeft
|
||
if ReplStubLayoutMode == 0 or pool is None or ch is None:
|
||
return 0
|
||
if src_sha1 is None or src_sha1[0] == '\0' or ReplStubTable is None:
|
||
return 0
|
||
# 存根文本(缺失:带父类/虚表的类将因"数据缺失"被明确拒绝,逐个可见)
|
||
txt: str = None
|
||
sp: str = _repl_stub_path(src_sha1, "stub.ll")
|
||
if sp is not None:
|
||
txt = _repl_stub_readtext(sp)
|
||
stdlib.free(sp)
|
||
have_stub: int = 0
|
||
if txt is not None:
|
||
have_stub = 1
|
||
else:
|
||
ReplStubNoStub += 1
|
||
if ReplStubWarnLeft > 0:
|
||
ReplStubWarnLeft -= 1
|
||
stdio.printf("includes 类布局: 模块 %s 无 .stub.ll 存根(数据缺失)—— 该模块"
|
||
"带父类/虚表的类将逐个按『数据缺失』明确拒绝(读不到存根 ⇒ 无"
|
||
"字段数可比,不是字段数不符)。用同一工程先跑一次批处理生成存根,"
|
||
"再 `--project .\\project.vpj --repl`(TransPyV 目录)即可复用\n",
|
||
src_sha1)
|
||
cnt: int = 0
|
||
i: sizet
|
||
for i in range(n):
|
||
child: ast.AST* = ch.get(i)
|
||
if child is None:
|
||
continue
|
||
if child.kind() != ast.ASTKind.ClassDef:
|
||
continue
|
||
cd: ast.ClassDef* = ast.ClassDef*(child)
|
||
if cd is None or cd.name is None:
|
||
continue
|
||
# 泛型模板类:与 _inject_class_struct_declare / 第一遍 A 同口径跳过(模板签名
|
||
# 含类级类型参数 T,注入时 T 无法确定)。但**归属模块必须登记**:REPL 会话不
|
||
# 注册 includes 泛型模板,故注解里的泛型下标(list[rawptr[AST]] 等)要靠
|
||
# "模板模块 sha1" 拼定义侧特化名造类型占位(TypeGenResolve 的 ReplCtx 分支)。
|
||
tp: list[str]* = cd.type_params
|
||
if tp is not None and len(tp) > 0:
|
||
StubMergerStore.RegisterGenericOwner(cd.name, src_sha1)
|
||
continue
|
||
# REnum 类:布局 {i32 tag, payload} 由 ClassGenEnum 计算,既有路径整体跳过
|
||
if _vpi_class_is_renum(cd) != 0:
|
||
continue
|
||
if _repl_stub_add_class(pool, mod, cd, src_sha1, txt, have_stub, trans,
|
||
imported_modules, from_imports) != 0:
|
||
cnt += 1
|
||
if txt is not None:
|
||
stdlib.free(txt)
|
||
return cnt
|
||
|
||
|
||
def ReplStubLayoutFinish() -> int:
|
||
"""收尾:收敛登记剩余类 + 打印汇总;返回已登记完整布局的类数"""
|
||
global ReplStubRefused, ReplStubWarnLeft
|
||
if ReplStubLayoutMode == 0:
|
||
return 0
|
||
rounds: int
|
||
for rounds in range(_REPL_STUB_FINISH_PASSES):
|
||
prog: int = 0
|
||
i: int
|
||
for i in range(ReplStubCount):
|
||
e: rawptr[ReplStubClass] = _repl_stub_at(i)
|
||
if e is None or e.State != _REPL_SS_PENDING:
|
||
continue
|
||
if _repl_stub_try(e, None) != 0:
|
||
prog += 1
|
||
if prog == 0:
|
||
break
|
||
# 仍未就绪 ⇒ 父类始终未注册(缺存根/被拒):明确拒绝(本类无法展平 ⇒ 不比较)
|
||
i2: int
|
||
for i2 in range(ReplStubCount):
|
||
e2: rawptr[ReplStubClass] = _repl_stub_at(i2)
|
||
if e2 is None or e2.State != _REPL_SS_PENDING:
|
||
continue
|
||
e2.State = _REPL_SS_REFUSED
|
||
# 找出仍未就绪的父类名(自证"上游是谁")
|
||
pbad: str = "(未定位)"
|
||
bi2: int
|
||
for bi2 in range(e2.BaseCount):
|
||
peb: rawptr[ReplStubClass] = _repl_stub_find_base(e2, bi2)
|
||
if peb is not None and peb.State == _REPL_SS_OK:
|
||
continue
|
||
if peb is not None and peb.ClassName is not None:
|
||
pbad = peb.ClassName
|
||
break
|
||
_repl_stub_warn_parent(e2, "父类未在会话注册或其布局被拒", pbad)
|
||
stdio.printf("includes 类布局: 完整布局 %d 个(其中虚表索引可用 %d 个)/ "
|
||
"被拒 %d 个 / 缺存根模块 %d 个\n",
|
||
ReplStubOk, ReplStubVtOk, ReplStubRefused, ReplStubNoStub)
|
||
stdio.printf(" 拒绝原因分布(自证数据来源):数据缺失-模块无 .stub.ll %d 个类 / "
|
||
"判据拒绝-存根无此类 %d 个 / 判据拒绝-存根为 opaque %d 个 / "
|
||
"上游无数据-父类不可用 %d 个\n",
|
||
ReplStubRefusedNoStub, ReplStubRefusedNoClass,
|
||
ReplStubRefusedOpaque, ReplStubRefusedParent)
|
||
if ReplStubRefused > 0 or ReplStubNoStub > 0:
|
||
stdio.printf(" 说明:被拒类的字段访问会报『类未注册/属性访问失败』"
|
||
"(不按错偏移静默访问);方法调用不受影响。\n")
|
||
if ReplStubRefusedNoStub > 0:
|
||
stdio.printf(" 其中『数据缺失』的 %d 个类只是没读到模块存根(本会话临时目录 %s "
|
||
"缺这些 .stub.ll):批处理才会落盘 .stub.ll(REPL 分支在 Phase1 之前"
|
||
"返回,故 --repl 自身不生成存根)。修法:用同一个工程跑一次批处理"
|
||
"(TransPyV 目录:.\\tpv_build\\output\\TransPyV.exe --project "
|
||
".\\project.vpj;includes 有改动时加 --rebuild-includes),随后 "
|
||
"--project .\\project.vpj --repl 复用其临时目录即可\n", ReplStubRefusedNoStub,
|
||
ReplStubTempDir)
|
||
if ReplStubRefusedNoClass + ReplStubRefusedOpaque > 0:
|
||
stdio.printf(" 其中『判据拒绝』的 %d 个类:数据在但不可信(存根里没有该类的字段数、"
|
||
"或其声明为 opaque),按 M4.2 一律不放行——请确认该类是否本应按结构体"
|
||
"登记:应按结构体登记而存根里查不到 ⇒ 存根陈旧/不完整(重建 includes "
|
||
"存根)\n", ReplStubRefusedNoClass + ReplStubRefusedOpaque)
|
||
return ReplStubOk
|
||
|
||
|
||
def ReplStubLayoutEnd() -> None:
|
||
"""关闭存根布局恢复(批处理路径此后完全不受影响)"""
|
||
global ReplStubLayoutMode
|
||
ReplStubLayoutMode = 0
|