4ed7f39 因 Ty 索引不等价,把名字索引的未命中短路一并回退了,导致 find_struct(8.25M)/lookup_ctypedef(5.64M) 等又回到全表扫描。
经逐处自证不变量后仅恢复名字键索引:名字或键只在注册时写入一次(中和仅置空名,空名查询显式回退扫描);索引在所有写入路径同步维护,判据 Built==Count 失败即失效回退;探测用 live 键且同键条目沿链按注册顺序排列(无空槽夹层)⇒ 可信未命中即原线性扫描亦无所获,短路逐字节等价。
恢复点:StructGenMixin.find_struct、TypeGenCType.lookup_ctypedef、CallGenLeafKit.find_func_in_table/find_func_entry_global、TypeGenCDef.lookup_cdefine_{constant,double,string}、VpiReader._lookup_vpi_param_annots/_lookup_vpi_ret_annot(后者保留泛型特化归一化兜底)。
find_struct_by_type 的 Ty 索引判定保持停用(事故根因);首字符预筛、Function.NameLen/FUNC_SIZE、includes 均未改动。
1952 lines
101 KiB
Plaintext
1952 lines
101 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import memhub
|
||
import string
|
||
import stdlib
|
||
import llvmlite
|
||
import stdio
|
||
import ast
|
||
import hashtable
|
||
import viperlib
|
||
import lib.core.CompilerFactory as CompilerFactory
|
||
import lib.core.VLogger as VLogger
|
||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||
import lib.core.Gen.GenTypes as GenTypes
|
||
import lib.core.Gen.Generator as Generator
|
||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||
import lib.core.Gen.GenApi as GenApi
|
||
|
||
# ============================================================
|
||
# StructGenMixin - 结构体注册/字段查找 Mixin(对象化 P2-2,wiki/14-oo-refactor.md)
|
||
#
|
||
# 从 HandlesStruct.vp 迁移:
|
||
# 全局状态(_struct_table/_struct_count/_g_stdlib_alloc/
|
||
# _visible_structs/_strict_visibility)→ 本类字段
|
||
# 模块级函数(register_struct/find_struct/lookup_field/...)→ 本类方法
|
||
#
|
||
#
|
||
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
|
||
# ============================================================
|
||
|
||
|
||
# ============================================================
|
||
# 常量(与 HandlesStruct 保持一致)
|
||
# ============================================================
|
||
STRUCT_MAX: t.CDefine = 512
|
||
# 名 | 类型 下标索引槽数(2 的幂且 > 2*STRUCT_MAX ⇒ 负载 < 0.5,探测必然终止)
|
||
STRUCT_IDX_CAP: t.CDefine = 1024
|
||
# 字段槽位上限制(与 HandlesStruct 保持一致;Generator 43 Mixin 展平 ~120 字段,
|
||
# 2026-08-16 追加78:64 截断导致第 9+ 父类字段(StmtGenMixin.DiagStmtCount 等)
|
||
# 丢失 → __init__ 写 self.DiagStmtCount "属性写入失败")
|
||
FIELD_MAX: t.CDefine = 256
|
||
FIELD_NAME_MAX: t.CDefine = 64
|
||
# 多继承(MRO)支持:单类最大父类数 / MRO 链最大长度(Generator 43 父类;
|
||
# PARENT_MAX=16 时第 17+ 父类字段丢失 → __init__ 写 self.DiagStmtCount 失败)
|
||
PARENT_MAX: t.CDefine = 64
|
||
MRO_MAX: t.CDefine = 64
|
||
|
||
# 索引哈希常量(32 位 FNV-1a,常量均 < 2^63 以避开大十进制字面量解析风险)
|
||
FNV32_OFFSET: t.CDefine = 2166136261
|
||
FNV32_PRIME: t.CDefine = 16777619
|
||
FNV32_MASK: t.CDefine = 4294967295
|
||
|
||
|
||
# ============================================================
|
||
# 索引哈希函数(名 | Ty 指针值 → 64 位哈希)
|
||
# 供 StructTable 的「名 | Ty 指针值 → 首注册下标」索引探测使用
|
||
# (见 _struct_name_idx_insert / _struct_name_idx_find 等)。
|
||
# ============================================================
|
||
def _struct_name_hash(name: str) -> uint64:
|
||
"""名字 → 哈希(32 位 FNV-1a,逐字节到 NUL 结束;空名返回偏移基值)"""
|
||
h: uint64 = uint64(FNV32_OFFSET)
|
||
if name is None:
|
||
return h
|
||
i: int = 0
|
||
while name[i] != '\0':
|
||
h = (h ^ uint64(int(name[i]))) * uint64(FNV32_PRIME)
|
||
h = h & uint64(FNV32_MASK)
|
||
i += 1
|
||
return h
|
||
|
||
|
||
def _struct_ty_hash(tyval: uint64) -> uint64:
|
||
"""Ty 指针值 → 哈希(对其 8 字节做 32 位 FNV-1a)"""
|
||
h: uint64 = uint64(FNV32_OFFSET)
|
||
v: uint64 = tyval
|
||
j: int = 0
|
||
while j < 8:
|
||
h = (h ^ (v & uint64(255))) * uint64(FNV32_PRIME)
|
||
h = h & uint64(FNV32_MASK)
|
||
v = v >> 8
|
||
j += 1
|
||
return h
|
||
|
||
|
||
|
||
# ============================================================
|
||
# StructGenMixin - 结构体注册表/字段查找/可见性
|
||
# ============================================================
|
||
@t.NoVTable
|
||
class StructGenMixin:
|
||
"""结构体注册与查找 Mixin(状态从 HandlesStruct 全局变量迁入)"""
|
||
|
||
# --- 全局注册表(原 _struct_table/_struct_count) ---
|
||
Gen: Generator.Generator | t.CPtr
|
||
StructTable: GenTypes.StructEntry | t.CPtr # STRUCT_MAX 槽位数组
|
||
StructCount: int
|
||
|
||
# --- OOM 回收分配模式(原 _g_stdlib_alloc) ---
|
||
StdlibAlloc: int
|
||
|
||
# --- 跨模块命名空间隔离(原 _visible_structs/_strict_visibility) ---
|
||
VisibleStructs: hashtable.HashTable | t.CPtr
|
||
StrictVisibility: int
|
||
|
||
# --- 名 | 类型 下标索引(查找快速路径;索引不可信时回退原线性扫描) ---
|
||
StructNameIdx: sizet | t.CPtr # 名 → 首注册下标(槽 u64:0=空槽,否则 index+1)
|
||
StructTypeIdx: sizet | t.CPtr # Ty 指针值 → 首注册下标(同布局)
|
||
StructIdxCap: int # 索引槽数(2 的幂,> 2*STRUCT_MAX)
|
||
StructIdxBuilt: int # 已同步建索引条目数(-1=不可信;= StructCount 才可信)
|
||
|
||
def InitStructGen(self) -> int:
|
||
"""初始化结构体 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
|
||
if self is None:
|
||
return 1
|
||
self.StructTable = None
|
||
self.StructCount = 0
|
||
self.StdlibAlloc = 0
|
||
self.VisibleStructs = None
|
||
self.StrictVisibility = 0
|
||
self.StructNameIdx = None
|
||
self.StructTypeIdx = None
|
||
self.StructIdxCap = 0
|
||
self.StructIdxBuilt = 0
|
||
return 0
|
||
|
||
# ============================================================
|
||
# P2-2 迁移方法(函数体来自 HandlesStruct.vp,全局变量改 self.字段,
|
||
# 同模块函数调用改 self.方法;签名与 HandlesStruct 原函数一致)
|
||
# ============================================================
|
||
|
||
def set_stdlib_alloc(self, flag: int) -> None:
|
||
"""设置 OOM 回收分配模式:1=1b 阶段(新条目用 stdlib),0=注册阶段(用 pool)"""
|
||
self.StdlibAlloc = flag
|
||
|
||
def get_stdlib_alloc(self) -> int:
|
||
"""读取 OOM 回收分配模式(供其他 Mixin 经 HandlesStruct 委托访问,
|
||
避免跨 Mixin 直接读 self.StdlibAlloc——Mixin 方法体按自身块内索引编译)"""
|
||
return self.StdlibAlloc
|
||
|
||
def reset_visible_structs(self, pool: rowptr[memhub.MemBuddy], strict: int) -> None:
|
||
"""重置可见性状态(每个文件翻译前调用)
|
||
|
||
pool: 内存分配器(严格模式下创建 HashTable)
|
||
strict: 1=严格模式(用户文件),0=宽松模式(includes 文件)
|
||
"""
|
||
self.StrictVisibility = strict
|
||
if strict != 0:
|
||
# 严格模式:创建新的 HashTable 记录可见结构体
|
||
self.VisibleStructs = hashtable.HashTable(pool)
|
||
else:
|
||
# 宽松模式:全部可见,不需要 HashTable
|
||
self.VisibleStructs = None
|
||
|
||
def add_visible_struct(self, pool: rowptr[memhub.MemBuddy], name: str) -> None:
|
||
"""添加可见结构体名(类定义时和 from-import 时调用)"""
|
||
if name is None:
|
||
return
|
||
# 宽松模式:全部可见,无需记录
|
||
if self.StrictVisibility == 0:
|
||
return
|
||
if self.VisibleStructs is None:
|
||
return
|
||
# 已存在则跳过(HashTable.__setitem__ 会覆盖,此处提前检查避免重复分配)
|
||
if self.VisibleStructs.__contains__(name) != 0:
|
||
return
|
||
self.VisibleStructs.set_int(name, 1)
|
||
|
||
def is_struct_visible(self, name: str) -> int:
|
||
"""检查结构体是否可见,返回 1=可见 / 0=不可见"""
|
||
if self.StrictVisibility == 0:
|
||
return 1
|
||
if name is None or self.VisibleStructs is None:
|
||
return 0
|
||
return self.VisibleStructs.__contains__(name)
|
||
|
||
def init_struct_table(self, pool: rowptr[memhub.MemBuddy]) -> int:
|
||
"""初始化结构体注册表,返回 1 成功
|
||
|
||
StdlibAlloc=1 时表体分配在 perm_pool:1a 每文件 reset 后表本体
|
||
不被回收(否则首次 register_struct 若发生在文件级 mark 之后,
|
||
reset 会把 StructTable 连根回收,全局 find_struct 悬空)。
|
||
"""
|
||
if self.StructTable is not None:
|
||
return 1
|
||
|
||
entry_size: sizet = GenTypes.StructEntry.__sizeof__()
|
||
table_pool: rowptr[memhub.MemBuddy] = pool
|
||
if self.StdlibAlloc != 0:
|
||
table_pool = CompilerFactory.factory.PermPool
|
||
if table_pool is None:
|
||
table_pool = pool
|
||
self.StructTable = table_pool.alloc(entry_size * STRUCT_MAX)
|
||
if self.StructTable is None:
|
||
return 0
|
||
string.memset(self.StructTable, 0, entry_size * STRUCT_MAX)
|
||
self.StructCount = 0
|
||
# 建 名 | 类型 索引(与表同池分配:表存活则索引存活,生命周期一致)。
|
||
# 索引是可选加速层:分配失败则保持 None 且 StructIdxBuilt=-1,查找自动
|
||
# 回退原线性扫描(语义完全一致),故不因此使初始化失败。
|
||
idx_bytes: sizet = sizet(STRUCT_IDX_CAP) * 8
|
||
name_idx: sizet | t.CPtr = table_pool.alloc(idx_bytes)
|
||
type_idx: sizet | t.CPtr = table_pool.alloc(idx_bytes)
|
||
if name_idx is None or type_idx is None:
|
||
self.StructNameIdx = None
|
||
self.StructTypeIdx = None
|
||
self.StructIdxCap = 0
|
||
self.StructIdxBuilt = -1
|
||
else:
|
||
string.memset(name_idx, 0, idx_bytes)
|
||
string.memset(type_idx, 0, idx_bytes)
|
||
self.StructNameIdx = name_idx
|
||
self.StructTypeIdx = type_idx
|
||
self.StructIdxCap = STRUCT_IDX_CAP
|
||
self.StructIdxBuilt = 0
|
||
return 1
|
||
|
||
def _get_struct_entry(self, i: int) -> GenTypes.StructEntry | t.CPtr:
|
||
"""获取第 i 个结构体条目"""
|
||
if self.StructTable is None or i < 0 or i >= STRUCT_MAX:
|
||
return None
|
||
entry_size: sizet = GenTypes.StructEntry.__sizeof__()
|
||
addr: uint64 = uint64(self.StructTable) + i * entry_size
|
||
return (GenTypes.StructEntry | t.CPtr)(t.CVoid(addr, t.CPtr))
|
||
|
||
# ============================================================
|
||
# 名 | 类型 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽)
|
||
#
|
||
# 目的:把 find_struct/find_struct_by_module(名)与 find_struct_by_type
|
||
# (类型指针)从 O(n) 全表扫描降为期望 O(1)。索引在注册处同步维护;
|
||
# 冲突逐键比较(名用 strcmp,类型用 Ty 指针值),不能只比哈希。
|
||
# 索引"可信"条件 = StructIdxBuilt == StructCount(每次注册后同步);
|
||
# 不可信/不可用时查找一律回退原线性扫描,保证语义逐字节一致。
|
||
# 条目被中和(Name[0]=0 / Ty=None,见 ClassGenDef/ClassGenEnum)后索引槽成为
|
||
# 失效键:查找探测时键不等 → 继续探测/判定未命中,不会返回失效条目。
|
||
# ============================================================
|
||
def _struct_entry_name(self, i: int) -> str:
|
||
"""取第 i 个条目的 Name(供索引探测比较),越界/空返回 None"""
|
||
e: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if e is None:
|
||
return None
|
||
return e.Name
|
||
|
||
def _struct_entry_tyval(self, i: int) -> uint64:
|
||
"""取第 i 个条目的 Ty 指针值(Ty=None 返回 0)"""
|
||
e: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if e is None or e.Ty is None:
|
||
return 0
|
||
return uint64(e.Ty)
|
||
|
||
def _struct_name_idx_insert(self, ei: int, name: str) -> int:
|
||
"""把 (name → ei) 写入名索引;返回 0=成功 / 1=失败
|
||
|
||
每次注册都占用一个槽(不按键去重):同键的多个条目按注册顺序沿探测链
|
||
递增排列,查找取链上首个"当前键相等"者即 = 原扫描的首个命中。若按键去重,
|
||
则最早的重复项被中和(Name[0]=0)后,被丢弃的后续重复项将无法找回。
|
||
"""
|
||
base: sizet | t.CPtr = self.StructNameIdx
|
||
if base is None or self.StructIdxCap <= 0:
|
||
return 1
|
||
h: uint64 = _struct_name_hash(name)
|
||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||
idx: sizet = sizet(h) & mask
|
||
i: int = 0
|
||
while i < self.StructIdxCap:
|
||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||
t.CVoid(uint64(base) + uint64(idx * 8), t.CPtr))
|
||
if slot[0] == 0:
|
||
slot[0] = sizet(ei + 1)
|
||
return 0
|
||
idx = (idx + 1) & mask
|
||
i += 1
|
||
return 1
|
||
|
||
def _struct_type_idx_insert(self, ei: int, tyval: uint64) -> int:
|
||
"""把 (Ty 指针值 → ei) 写入类型索引;0=成功 / 1=失败
|
||
|
||
与名索引同策略:每次注册都占槽(不去重),保证探测链上同键条目按注册
|
||
顺序排列,查找首个"当前 Ty 相等"者 = 原扫描首个命中。
|
||
"""
|
||
base: sizet | t.CPtr = self.StructTypeIdx
|
||
if base is None or self.StructIdxCap <= 0 or tyval == 0:
|
||
return 1
|
||
h: uint64 = _struct_ty_hash(tyval)
|
||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||
idx: sizet = sizet(h) & mask
|
||
i: int = 0
|
||
while i < self.StructIdxCap:
|
||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||
t.CVoid(uint64(base) + uint64(idx * 8), t.CPtr))
|
||
if slot[0] == 0:
|
||
slot[0] = sizet(ei + 1)
|
||
return 0
|
||
idx = (idx + 1) & mask
|
||
i += 1
|
||
return 1
|
||
|
||
def _struct_idx_add(self, ei: int, entry: GenTypes.StructEntry | t.CPtr) -> None:
|
||
"""新增条目后同步两张索引;任一写入失败 → 标记索引不可信(查找回退扫描)"""
|
||
if self.StructNameIdx is None or self.StructTypeIdx is None or self.StructIdxCap <= 0:
|
||
self.StructIdxBuilt = -1
|
||
return
|
||
failed: int = 0
|
||
if entry is not None and entry.Name is not None:
|
||
failed = failed + self._struct_name_idx_insert(ei, entry.Name)
|
||
if entry is not None and entry.Ty is not None:
|
||
failed = failed + self._struct_type_idx_insert(ei, uint64(entry.Ty))
|
||
if failed != 0:
|
||
self.StructIdxBuilt = -1
|
||
else:
|
||
# ei = StructCount-1,故同步后已建索引条目数 = StructCount
|
||
self.StructIdxBuilt = self.StructCount
|
||
|
||
def _struct_name_idx_find(self, name: str) -> int:
|
||
"""名索引查首注册下标:>=0 命中(= 原扫描首个同名条目下标);否则 -1
|
||
|
||
-1 表示"未命中或索引不可用",调用方须回退原线性扫描(保证正确性)。
|
||
探测链上同键条目按注册顺序排列,故首个"当前键相等"者即原扫描首个命中。
|
||
"""
|
||
base: sizet | t.CPtr = self.StructNameIdx
|
||
if base is None or self.StructIdxCap <= 0 or self.StructIdxBuilt != self.StructCount:
|
||
return -1
|
||
# 空名("")特例:被中和的条目 Name 恰为 ""(Name[0]=0),原扫描会命中首个
|
||
# 空名条目;索引槽按"注册时的原名"哈希放置,无法保证给出首个空名条目 →
|
||
# 空名一律回退原扫描,保证逐字节一致。
|
||
if name is None or name[0] == '\0':
|
||
return -1
|
||
h: uint64 = _struct_name_hash(name)
|
||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||
idx: sizet = sizet(h) & mask
|
||
i: int = 0
|
||
while i < self.StructIdxCap:
|
||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||
t.CVoid(uint64(base) + uint64(idx * 8), t.CPtr))
|
||
sv: sizet = slot[0]
|
||
if sv == 0:
|
||
return -1
|
||
se: int = int(sv) - 1
|
||
ek: str = self._struct_entry_name(se)
|
||
if ek is not None and string.strcmp(ek, name) == 0:
|
||
return se
|
||
idx = (idx + 1) & mask
|
||
i += 1
|
||
return -1
|
||
|
||
def _struct_type_idx_find(self, tyval: uint64) -> int:
|
||
"""类型索引查首注册下标:>=0 命中;-1 未命中/不可信(须回退扫描)
|
||
|
||
类型索引不保证覆盖全部 Ty 值(如 ClassGenDef 深拷贝后 existing.Ty 被
|
||
改写为新指针,未入索引)⇒ 未命中不能断言"确无",一律回退线性扫描。
|
||
"""
|
||
base: sizet | t.CPtr = self.StructTypeIdx
|
||
if base is None or self.StructIdxCap <= 0 or tyval == 0:
|
||
return -1
|
||
if self.StructIdxBuilt != self.StructCount:
|
||
return -1
|
||
h: uint64 = _struct_ty_hash(tyval)
|
||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||
idx: sizet = sizet(h) & mask
|
||
i: int = 0
|
||
while i < self.StructIdxCap:
|
||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||
t.CVoid(uint64(base) + uint64(idx * 8), t.CPtr))
|
||
sv: sizet = slot[0]
|
||
if sv == 0:
|
||
return -1
|
||
se: int = int(sv) - 1
|
||
if self._struct_entry_tyval(se) == tyval:
|
||
return se
|
||
idx = (idx + 1) & mask
|
||
i += 1
|
||
return -1
|
||
|
||
def _get_field_entry(self, struct_entry: GenTypes.StructEntry | t.CPtr, i: int) -> GenTypes.FieldEntry | t.CPtr:
|
||
"""获取结构体中第 i 个字段条目"""
|
||
if struct_entry is None or i < 0 or i >= FIELD_MAX:
|
||
return None
|
||
field_size: sizet = GenTypes.FieldEntry.__sizeof__()
|
||
addr: uint64 = uint64(struct_entry.Fields) + i * field_size
|
||
return (GenTypes.FieldEntry | t.CPtr)(t.CVoid(addr, t.CPtr))
|
||
|
||
def register_struct(self, pool: rowptr[memhub.MemBuddy],
|
||
name: str,
|
||
struct_ty: llvmlite.LLVMType | t.CPtr,
|
||
sha1: str = None) -> GenTypes.StructEntry | t.CPtr:
|
||
"""注册结构体类型,返回 StructEntry 指针
|
||
|
||
用类型指针去重(is 比较),不用类名去重。
|
||
这样跨模块同名类可以共存,各自有独立的字段定义。
|
||
sha1 参数在注册时直接设置 ModuleSha1(避免 set_struct_sha1 找错 entry)。
|
||
"""
|
||
if self.init_struct_table(pool) == 0:
|
||
return None
|
||
|
||
# 用类型指针去重(同一类型不重复注册,支持跨模块同名类)
|
||
# 用 uint64 指针值比较(is 比较会触发类型安全检查解引用悬空 Ty)
|
||
# 走 find_struct_by_type(其 Ty 索引判定已整体停用、恢复原线性扫描),
|
||
# 去重语义 = 原去重扫描"首个 Ty 指针相等条目"(首注册优先),逐字节一致。
|
||
existing: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||
if existing is not None:
|
||
return existing
|
||
|
||
if self.StructCount >= STRUCT_MAX:
|
||
VLogger.error(f"table full, cannot register {name}", "STRUCT")
|
||
return None
|
||
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(self.StructCount)
|
||
if entry is None:
|
||
return None
|
||
|
||
# 分配字段数组(1b 阶段用永久池,避免 reset 回收后 Fields 悬空)
|
||
field_size: sizet = GenTypes.FieldEntry.__sizeof__()
|
||
if self.StdlibAlloc != 0:
|
||
perm_sg: rowptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
|
||
if perm_sg is None:
|
||
perm_sg = pool
|
||
entry.Fields = perm_sg.alloc(field_size * FIELD_MAX)
|
||
else:
|
||
entry.Fields = pool.alloc(field_size * FIELD_MAX)
|
||
if entry.Fields is None:
|
||
return None
|
||
string.memset(entry.Fields, 0, field_size * FIELD_MAX)
|
||
|
||
# 复制类名(1b/Phase2(StdlibAlloc=1)用永久池跨 reset 存活;
|
||
# 1a(StdlibAlloc=0)用主 pool,mark1 前不回收同样存活。
|
||
# 此前 1a 用 pool、1b 用 stdlib,find_struct_by_module 的 strcmp
|
||
# 曾读悬空 Name 越界 → G3 LinkedNode.prepend 挂起根因)
|
||
name_pool: rowptr[memhub.MemBuddy] = pool
|
||
if self.StdlibAlloc != 0:
|
||
name_pool = CompilerFactory.factory.PermPool
|
||
if name_pool is None:
|
||
name_pool = pool
|
||
name_len: sizet = string.strlen(name)
|
||
name_buf: str = name_pool.alloc(name_len + 1)
|
||
if name_buf is not None:
|
||
string.strcpy(name_buf, name)
|
||
entry.Name = name_buf
|
||
|
||
entry.Ty = struct_ty
|
||
entry.FieldCount = 0
|
||
|
||
# 注册时直接设置 SHA1(避免 set_struct_sha1 跨模块同名时找错 entry)
|
||
# SHA1 同样按 StdlibAlloc 选分配源(跨 1a/1b 存活,防 strcmp 悬空)
|
||
if sha1 is not None:
|
||
sha1_len: sizet = string.strlen(sha1)
|
||
sha1_buf: str = name_pool.alloc(sha1_len + 1)
|
||
if sha1_buf is not None:
|
||
string.strcpy(sha1_buf, sha1)
|
||
entry.ModuleSha1 = sha1_buf
|
||
|
||
self.StructCount += 1
|
||
# 同步名 | 类型索引(新条目下标 = StructCount-1)
|
||
self._struct_idx_add(self.StructCount - 1, entry)
|
||
return entry
|
||
|
||
def register_struct_alias(self, pool: rowptr[memhub.MemBuddy],
|
||
name: str,
|
||
sha1: str,
|
||
src_entry: GenTypes.StructEntry | t.CPtr) -> GenTypes.StructEntry | t.CPtr:
|
||
"""为已注册类追加包重导出别名 entry(方案A:ast.Attribute 归属 ast 包)
|
||
|
||
子模块类从包 __init__ 重导出时,查找端 _resolve_module_sha1 按
|
||
"__init__ 包回退"解析为包 sha1,但类以定义文件 sha1 注册 → name+sha1
|
||
双精确匹配错位 fail-fast(lookup_field_by_class 'ast.Attribute' 未找到
|
||
根因)。本函数绕过 register_struct 的 Ty 去重,追加同名 entry:
|
||
ModuleSha1=包 sha1,Ty/Fields/父类/虚表标志复用源 entry(同一类型,
|
||
仅归属模块不同,不影响 Ty 去重与 find_struct_by_type 指针匹配)。
|
||
|
||
幂等:目标 (name, sha1) 已注册则返回现有 entry。返回 None 失败。
|
||
"""
|
||
if name is None or sha1 is None or src_entry is None:
|
||
return None
|
||
if self.init_struct_table(pool) == 0:
|
||
return None
|
||
# 幂等:目标 (name, sha1) 已注册则跳过(find_struct_by_module 不回退裸名)
|
||
if sha1[0] != '\0':
|
||
existing: GenTypes.StructEntry | t.CPtr = self.find_struct_by_module(name, sha1)
|
||
if existing is not None:
|
||
return existing
|
||
if self.StructCount >= STRUCT_MAX:
|
||
return None
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(self.StructCount)
|
||
if entry is None:
|
||
return None
|
||
# 复制类名与 SHA1(别名注册同样按 StdlibAlloc 选分配源,跨 1a/1b reset 存活)
|
||
alias_pool: rowptr[memhub.MemBuddy] = pool
|
||
if self.StdlibAlloc != 0:
|
||
alias_pool = CompilerFactory.factory.PermPool
|
||
if alias_pool is None:
|
||
alias_pool = pool
|
||
name_len: sizet = string.strlen(name)
|
||
name_buf: str = alias_pool.alloc(name_len + 1)
|
||
if name_buf is None:
|
||
return None
|
||
string.strcpy(name_buf, name)
|
||
entry.Name = name_buf
|
||
sha1_len: sizet = string.strlen(sha1)
|
||
sha1_buf: str = alias_pool.alloc(sha1_len + 1)
|
||
if sha1_buf is None:
|
||
return None
|
||
string.strcpy(sha1_buf, sha1)
|
||
entry.ModuleSha1 = sha1_buf
|
||
# 复用源 entry 的类型、字段与类属性(同一类,仅归属模块不同)
|
||
entry.Ty = src_entry.Ty
|
||
entry.FieldCount = src_entry.FieldCount
|
||
entry.Fields = src_entry.Fields
|
||
entry.IsUnion = src_entry.IsUnion
|
||
entry.IsPacked = src_entry.IsPacked
|
||
entry.IsOOP = src_entry.IsOOP
|
||
entry.HasInit = src_entry.HasInit
|
||
entry.HasNew = src_entry.HasNew
|
||
entry.HasVTable = src_entry.HasVTable
|
||
entry.IsNoVTable = src_entry.IsNoVTable
|
||
entry.ParentName = src_entry.ParentName
|
||
entry.ParentNames = src_entry.ParentNames
|
||
entry.ParentCount = src_entry.ParentCount
|
||
entry.VTableMethodCount = src_entry.VTableMethodCount
|
||
entry.VTableMethods = src_entry.VTableMethods
|
||
entry.MethodsEmitted = 0
|
||
entry.SpecTriggerSha1 = None
|
||
self.StructCount += 1
|
||
# 同步名 | 类型索引(别名条目与源条目同名 | 同 Ty,索引保留首注册下标)
|
||
self._struct_idx_add(self.StructCount - 1, entry)
|
||
return entry
|
||
|
||
def set_struct_sha1(self, pool: rowptr[memhub.MemBuddy],
|
||
class_name: str, sha1: str) -> int:
|
||
"""设置结构体所属模块的 SHA1,返回 1=成功 / 0=失败"""
|
||
if class_name is None or sha1 is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(class_name)
|
||
if entry is None:
|
||
return 0
|
||
# 复制 SHA1 字符串(StdlibAlloc=1 用永久池,跨 1a/1b 存活防 strcmp 悬空)
|
||
sha1_len: sizet = string.strlen(sha1)
|
||
sha1_pool: rowptr[memhub.MemBuddy] = pool
|
||
if self.StdlibAlloc != 0:
|
||
sha1_pool = CompilerFactory.factory.PermPool
|
||
if sha1_pool is None:
|
||
sha1_pool = pool
|
||
sha1_buf: str = sha1_pool.alloc(sha1_len + 1)
|
||
if sha1_buf is None:
|
||
return 0
|
||
string.strcpy(sha1_buf, sha1)
|
||
entry.ModuleSha1 = sha1_buf
|
||
return 1
|
||
|
||
def get_struct_sha1(self, class_name: str) -> str:
|
||
"""获取结构体所属模块的 SHA1,返回 None=无 SHA1"""
|
||
if class_name is None or self.StructTable is None:
|
||
return None
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(class_name)
|
||
if entry is None:
|
||
return None
|
||
return entry.ModuleSha1
|
||
|
||
def add_field(self, pool: rowptr[memhub.MemBuddy],
|
||
struct_entry: GenTypes.StructEntry | t.CPtr,
|
||
field_name: str,
|
||
field_ty: llvmlite.LLVMType | t.CPtr,
|
||
default_val: ast.AST | t.CPtr = None,
|
||
annot_class_name: str = None) -> int:
|
||
"""向结构体添加字段,返回字段索引(-1 失败)
|
||
|
||
default_val: 字段默认值 AST 节点(可选,None=无默认值)
|
||
annot_class_name: 原始类型注解的类名(可选,用于联合类型字段的方法调用解析)
|
||
"""
|
||
if struct_entry is None or field_name is None or field_ty is None:
|
||
return -1
|
||
|
||
if struct_entry.FieldCount >= FIELD_MAX:
|
||
VLogger.error(f"field table full for {struct_entry.Name}", "STRUCT")
|
||
return -1
|
||
|
||
idx: int = struct_entry.FieldCount
|
||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(struct_entry, idx)
|
||
if fe is None:
|
||
return -1
|
||
|
||
# 复制字段名(StdlibAlloc=1 用永久池跨 reset 存活,
|
||
# 否则 lookup_field 的 strcmp(fe.Name, ...) 读悬空越界挂起,
|
||
# 与 register_struct 的 Name 悬空同根因)
|
||
fname_pool: rowptr[memhub.MemBuddy] = pool
|
||
if self.StdlibAlloc != 0:
|
||
fname_pool = CompilerFactory.factory.PermPool
|
||
if fname_pool is None:
|
||
fname_pool = pool
|
||
name_len: sizet = string.strlen(field_name)
|
||
name_buf: str = fname_pool.alloc(name_len + 1)
|
||
if name_buf is not None:
|
||
string.strcpy(name_buf, field_name)
|
||
fe.Name = name_buf
|
||
|
||
fe.Index = idx
|
||
# 1b 阶段(StdlibAlloc=1)字段类型必须深拷贝到 stdlib:
|
||
# register_struct 已迁 Fields/Name/ModuleSha1/Ty(translate_class_def L2566
|
||
# DeepCopyType),但字段级 Ty 仍存 pool 指针,本文件 reset_to(mark1) 回收后,
|
||
# 跨文件读取(字段展平、多继承 this 调整、_apply_struct_defaults 的
|
||
# build_gep_struct 用 fe.Ty 作 GEP 结果类型)解引用悬空内存 → 崩溃点漂移
|
||
# (json/string/builtins 之间)。
|
||
# 注:注解类名(原 fe.AnnotClassName)已并入 Spec.SourceName,Spec 一律
|
||
# PermPool 分配 ⇒ 天然跨 reset 存活,无需在此深拷贝。
|
||
if self.StdlibAlloc != 0:
|
||
dp_pool_sg: rowptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
|
||
if dp_pool_sg is None:
|
||
dp_pool_sg = pool
|
||
ty_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(dp_pool_sg, field_ty)
|
||
if ty_copy is not None:
|
||
fe.Ty = ty_copy
|
||
else:
|
||
fe.Ty = field_ty
|
||
# DefaultVal:跨文件结构体构造(_translate_struct_ctor→_apply_struct_defaults)
|
||
# 在调用方文件翻译时读取字段默认值,原 pool AST 已 reset 回收 → 常量深拷贝
|
||
# 到 perm_pool(1a 每文件 reset 后 DefaultVal 同样必须跨文件存活)
|
||
fe.DefaultVal = self._deep_copy_default_ast(dp_pool_sg, default_val)
|
||
else:
|
||
fe.Ty = field_ty
|
||
fe.DefaultVal = default_val
|
||
|
||
# [S3 条目双写] 字段类型 Spec(唯一类型承载,含注解来源名)。TypeSpec
|
||
# 实例一律 PermPool 分配(驻留或独立副本)⇒ 天然跨 1b reset 存活。
|
||
arena_f: TSpec.TypeSpecArena | t.CPtr = self.Gen.TypeGenBase.ensure_spec_arena()
|
||
if arena_f is not None:
|
||
fe.Spec = TSpec.SpecFromLLVMType(arena_f, field_ty)
|
||
# [S4 注解侧填 Spec] 补字段 Spec 的符号性与注解来源名(annot_class_name
|
||
# 携带真实无符号性与注解书写形态名;LLVM 反推恒 TS_INT 且无名)。
|
||
# 类型信息缺失(Spec 为 None)时建 TS_UNKNOWN 承载注解名(不表态具体类型)。
|
||
if annot_class_name is not None:
|
||
if fe.Spec is None:
|
||
fe.Spec = TSpec.NewTypeSpec(arena_f, TSpec.TS_UNKNOWN, 0,
|
||
TSpec.TS_SIGN_UNKNOWN)
|
||
if fe.Spec is not None:
|
||
sign_f: int = self.Gen.TypeGenCType.spec_sign_of_annot(annot_class_name)
|
||
if sign_f != TSpec.TS_SIGN_UNKNOWN:
|
||
fe.Spec = TSpec.SpecWithSign(arena_f, fe.Spec, sign_f)
|
||
fe.Spec = TSpec.SpecWithSource(fe.Spec, annot_class_name)
|
||
|
||
struct_entry.FieldCount = idx + 1
|
||
return idx
|
||
|
||
def _deep_copy_default_ast(self, pool: rowptr[memhub.MemBuddy],
|
||
default_val: ast.AST | t.CPtr) -> ast.AST | t.CPtr:
|
||
"""深拷贝字段默认值 AST 到永久池(仅常量节点),跨 1b reset 存活。
|
||
|
||
字段默认值(fe.DefaultVal)除定义文件内 _generate_before_init 使用外,
|
||
跨文件结构体构造(_translate_struct_ctor → _apply_struct_defaults)会在
|
||
调用方文件翻译时读取——原 pool AST 已 reset 回收必须深拷贝。
|
||
支持 Constant(标量)与 List(列表字面量,如 t.CArray[str] = [...])。
|
||
其他非常量默认值(Name/BinOp 等引用表达式)无法安全深拷贝:返回 None
|
||
(调用方 fe.DefaultVal 保持 None,仅定义文件内 _generate_before_init
|
||
仍持有原始 pool 指针,不受影响)。
|
||
"""
|
||
if default_val is None:
|
||
return None
|
||
if default_val.kind() == ast.ASTKind.List:
|
||
return self._deep_copy_list_ast(pool, default_val)
|
||
if default_val.kind() != ast.ASTKind.Constant:
|
||
return None
|
||
src_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(default_val)
|
||
dst_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(
|
||
pool.alloc(ast.Constant.__sizeof__()))
|
||
if dst_c is None:
|
||
return None
|
||
string.memset(dst_c, 0, ast.Constant.__sizeof__())
|
||
# 拷贝 vtable 指针(AST 是 @t.CVTable,kind() 虚调用依赖首字段 vptr)
|
||
vt_ptr: t.CPtr = t.CVoid(
|
||
c.Deref(uint64(default_val, t.CPtr), t.CPtr), t.CPtr)
|
||
c.DerefAs(dst_c, vt_ptr)
|
||
# 拷贝位置字段(报错定位用)
|
||
dst_c.lineno = src_c.lineno
|
||
dst_c.col_offset = src_c.col_offset
|
||
dst_c.end_lineno = src_c.end_lineno
|
||
dst_c.end_col_offset = src_c.end_col_offset
|
||
# 拷贝常量值(int/float/str/bool 由 const_kind 区分)
|
||
dst_c.const_kind = src_c.const_kind
|
||
dst_c.int_val = src_c.int_val
|
||
dst_c.float_val = src_c.float_val
|
||
if src_c.str_val is not None:
|
||
s_len: sizet = string.strlen(src_c.str_val)
|
||
s_copy: str = pool.alloc(s_len + 1)
|
||
if s_copy is not None:
|
||
string.strcpy(s_copy, src_c.str_val)
|
||
dst_c.str_val = s_copy
|
||
return (ast.AST | t.CPtr)(dst_c)
|
||
|
||
def _deep_copy_list_ast(self, pool: rowptr[memhub.MemBuddy],
|
||
default_val: ast.AST | t.CPtr) -> ast.AST | t.CPtr:
|
||
"""深拷贝 List 节点(列表字面量默认值)到永久池,跨 1b reset 存活。
|
||
|
||
elts 容器手动在永久池构造(list 的 __pool__ 置 None:深拷贝的 elts
|
||
只被只读访问 get/__len__/迭代,不 append,不会触发 pool 扩容)。
|
||
元素递归调用 _deep_copy_default_ast(支持 Constant 与嵌套 List)。
|
||
"""
|
||
src_l: ast.List | t.CPtr = (ast.List | t.CPtr)(default_val)
|
||
dst_l: ast.List | t.CPtr = (ast.List | t.CPtr)(
|
||
pool.alloc(ast.List.__sizeof__()))
|
||
if dst_l is None:
|
||
return None
|
||
string.memset(dst_l, 0, ast.List.__sizeof__())
|
||
# 拷贝 vtable 指针(AST 是 @t.CVTable,kind() 虚调用依赖首字段 vptr)
|
||
vt_l: t.CPtr = t.CVoid(
|
||
c.Deref(uint64(default_val, t.CPtr), t.CPtr), t.CPtr)
|
||
c.DerefAs(dst_l, vt_l)
|
||
# 拷贝位置字段与 ctx
|
||
dst_l.lineno = src_l.lineno
|
||
dst_l.col_offset = src_l.col_offset
|
||
dst_l.end_lineno = src_l.end_lineno
|
||
dst_l.end_col_offset = src_l.end_col_offset
|
||
dst_l.ctx = src_l.ctx
|
||
# 深拷贝 elts:stdlib 手动构造 list[AST | t.CPtr] 容器
|
||
src_elts_l: list[ast.AST | t.CPtr] | t.CPtr = src_l.elts
|
||
if src_elts_l is not None:
|
||
cnt_l: sizet = len(src_elts_l)
|
||
lst_copy_l: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)(
|
||
pool.alloc(list[ast.AST | t.CPtr].__sizeof__()))
|
||
if lst_copy_l is not None:
|
||
string.memset(lst_copy_l, 0, list[ast.AST | t.CPtr].__sizeof__())
|
||
lst_copy_l.__count__ = cnt_l
|
||
lst_copy_l.__capacity__ = cnt_l
|
||
lst_copy_l.__elem_size__ = 8
|
||
lst_copy_l.__pool__ = None
|
||
data_l: t.CPtr = pool.alloc(cnt_l * 8)
|
||
if data_l is not None:
|
||
string.memset(data_l, 0, cnt_l * 8)
|
||
lst_copy_l.__data__ = data_l
|
||
li_l: sizet
|
||
for li_l in range(cnt_l):
|
||
elem_src_l: ast.AST | t.CPtr = src_elts_l.get(li_l)
|
||
if elem_src_l is not None:
|
||
elem_dst_l: ast.AST | t.CPtr = self._deep_copy_default_ast(pool, elem_src_l)
|
||
if elem_dst_l is not None:
|
||
slot_l: ast.AST | t.CPtr = (ast.AST | t.CPtr)(
|
||
t.CVoid(uint64(data_l) + li_l * 8, t.CPtr))
|
||
slot_l[0] = elem_dst_l
|
||
dst_l.elts = lst_copy_l
|
||
return (ast.AST | t.CPtr)(dst_l)
|
||
|
||
def get_struct_count(self) -> int:
|
||
"""返回当前已注册的结构体数量"""
|
||
return self.StructCount
|
||
|
||
def _resolve_module_sha1(self, mod_name: str,
|
||
from_imports: str = None) -> str:
|
||
"""模块名 → 模块 SHA1(精确 → import 别名还原后再查 → 后缀兜底)
|
||
|
||
模块名可能是:模块真名(fat32_types)、包后缀(sched.sched)、
|
||
import 别名(import X as Y 的 Y)。逐级尝试:
|
||
1. LookupSha1ByModName:精确 + "__init__" 包回退
|
||
2. LookupModNameByAlias:from_imports "别名:模块真名" 还原后
|
||
精确/后缀重试——必须优先于裸名后缀匹配:store 可能同时存在
|
||
"vpsdk.process" 与 "VKernel.Kernel.sched.process",裸名
|
||
"process" 后缀匹配会命中先扫描的内核模块(别名 process 被
|
||
劫持 → 跨模块调用解析到未编译的内核 → Phase A _deps_pending
|
||
永不收敛,Scene3D root cause)
|
||
3. LookupSha1ByModNameSuffix:后缀匹配(store 键是全路径模块名,
|
||
如 "Kernel.drivers.fs.fat32.fat32_types"),非别名裸名兜底
|
||
"""
|
||
if mod_name is None:
|
||
return None
|
||
s_rm: str = StubMergerStore.LookupSha1ByModName(mod_name)
|
||
if s_rm is None and from_imports is not None:
|
||
real_rm: str = StubMergerStore.LookupModNameByAlias(from_imports, mod_name)
|
||
if real_rm is not None:
|
||
s_rm = StubMergerStore.LookupSha1ByModName(real_rm)
|
||
if s_rm is None:
|
||
s_rm = StubMergerStore.LookupSha1ByModNameSuffix(real_rm)
|
||
if s_rm is None:
|
||
s_rm = StubMergerStore.LookupSha1ByModNameSuffix(mod_name)
|
||
return s_rm
|
||
|
||
def find_struct(self, name: str) -> GenTypes.StructEntry | t.CPtr:
|
||
"""按类名查找结构体,返回 StructEntry 或 None
|
||
|
||
支持模块限定名(如 "HT.Translator"):
|
||
第一遍精确匹配,第二遍按类名(最后一个 '.' 之后的部分)回退。
|
||
模块可解析的限定名(module.Class)按模块归属精确匹配,不做裸名
|
||
跨库回退(防 sched.Thread 被 vthreading.Thread 顶掉的同类问题);
|
||
仅模块名无法解析时保留裸名回退(兼容旧行为)。
|
||
"""
|
||
if name is None or self.StructTable is None:
|
||
return None
|
||
# 第一遍:精确匹配
|
||
# StructCount 合理性守卫(内存损坏导致巨大值 → 索引循环假挂起)
|
||
if self.StructCount < 0 or self.StructCount > 200000:
|
||
VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct)", "STRUCT")
|
||
stdlib.exit(1)
|
||
# 名索引快速路径:命中即首注册下标(语义 = 原精确匹配扫描首个命中)。
|
||
fi: int = self._struct_name_idx_find(name)
|
||
if fi >= 0:
|
||
return self._get_struct_entry(fi)
|
||
# 名索引可信(Built==StructCount | 索引存在 | 查询名非空)时,索引未命中即
|
||
# "确无同名条目":条目在注册时即命名并同步入索引,之后名至多被中和为 ""
|
||
# (ClassGenDef/ClassGenEnum 置 Name[0]=0),故任何非空查询名的命中项必在
|
||
# 索引探测链上 ⇒ 可跳过下方 O(n) 精确扫描(热点:本扫描占 find_struct 的
|
||
# 全部 strcmp)。索引不可信 / 无索引 / 空名查询时仍回退原线性扫描
|
||
# (正确性保证,非兜底);精确匹配确无时继续走模块限定名/裸名回退逻辑。
|
||
name_idx_trusted: int = 0
|
||
if self.StructNameIdx is not None and self.StructIdxCap > 0 \
|
||
and self.StructIdxBuilt == self.StructCount:
|
||
name_idx_trusted = 1
|
||
if name_idx_trusted == 0 or name[0] == '\0':
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if entry is not None and entry.Name is not None:
|
||
if string.strcmp(entry.Name, name) == 0:
|
||
return entry
|
||
# 模块限定名(module.Class):模块可解析时按模块归属精确匹配,
|
||
# 防止裸名回退抓跨模块同名类。模块未命中时若裸名类唯一(无冲突)
|
||
# 仍回退,避免误杀未登记模块的既有解析。
|
||
# 取点跳过泛型下标(numpy.ndarray[t.CDouble] → numpy + ndarray[t.CDouble]),
|
||
# 否则 strrchr 落到下标内部截错模块名(同 lookup_field_by_class 根因)。
|
||
dot_pos_q: str = self._last_dot_outside_brackets(name)
|
||
if dot_pos_q is not None and dot_pos_q[1] != '\0':
|
||
bare_f: str = dot_pos_q + 1
|
||
mod_len_f: sizet = string.strlen(name) - string.strlen(bare_f) - 1
|
||
if mod_len_f > 0:
|
||
mod_buf_f: str = stdlib.malloc(mod_len_f + 1)
|
||
if mod_buf_f is not None:
|
||
string.strncpy(mod_buf_f, name, mod_len_f)
|
||
mod_buf_f[mod_len_f] = '\0'
|
||
if string.strcmp(mod_buf_f, "t") != 0 and string.strcmp(mod_buf_f, "c") != 0:
|
||
# 模块段是 16 位 hex = SHA1 字面量(如 "{9aab}.Scene3D",
|
||
# TypeGenResolve L1397-1402 的 sha1 前缀形态):跳过 store
|
||
# 模块名查询(store 键是模块名不是 sha1),直接按 name+sha1
|
||
# 精确匹配。否则 _resolve_module_sha1("9aab") 返回 None →
|
||
# 落到第二遍裸名回退 → 跨模块同名类(VKernel gfx 与 App gfx
|
||
# 都定义 Scene3D)返回先注册的错误 entry(Scene3D 归属错乱
|
||
# → ret_sha1=0ae → Phase A _deps_pending 死锁根因)。
|
||
# [C10 收敛] 模块段恰为 16 位小写 hex = SHA1 字面量:
|
||
# 统一走 GenApi.is_sha1_hex16(该判定的唯一真源)。
|
||
mod_hex_ok: int = GenApi.is_sha1_hex16(mod_buf_f)
|
||
if mod_hex_ok != 0:
|
||
me_h: GenTypes.StructEntry | t.CPtr = \
|
||
self.find_struct_by_module(bare_f, mod_buf_f)
|
||
stdlib.free(mod_buf_f)
|
||
return me_h
|
||
mod_sha1_f: str = self._resolve_module_sha1(mod_buf_f)
|
||
if mod_sha1_f is not None:
|
||
me_f: GenTypes.StructEntry | t.CPtr = \
|
||
self.find_struct_by_module(bare_f, mod_sha1_f)
|
||
if me_f is not None:
|
||
stdlib.free(mod_buf_f)
|
||
return me_f
|
||
# 模块类未注册:统计裸名同名类数量,唯一时回退(无冲突)
|
||
same_cnt_f: int = 0
|
||
same_entry_f: GenTypes.StructEntry | t.CPtr = None
|
||
for i2 in range(self.StructCount):
|
||
e2: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i2)
|
||
if e2 is not None and e2.Name is not None:
|
||
if string.strcmp(e2.Name, bare_f) == 0:
|
||
same_cnt_f += 1
|
||
same_entry_f = e2
|
||
stdlib.free(mod_buf_f)
|
||
if same_cnt_f == 1:
|
||
return same_entry_f
|
||
return None
|
||
stdlib.free(mod_buf_f)
|
||
# 第二遍:类名回退(处理模块限定名 "HT.Translator" → "Translator",
|
||
# 仅模块名无法解析时走到这里;取点同样跳过泛型下标)
|
||
dot_pos: str = self._last_dot_outside_brackets(name)
|
||
if dot_pos is not None:
|
||
class_name: str = dot_pos + 1
|
||
if class_name is not None and class_name[0] != '\0':
|
||
# 同名唯一性检查(对齐上方限定名路径 L543-554):跨模块同名类
|
||
# (如 VKernel gfx 与 App gfx 都定义 Scene3D)存在时不得返回
|
||
# 先注册的错误 entry——否则模块段无法解析的限定名(如
|
||
# "9aab.Scene3D")被劫持到错误模块 → 返回类型解析查错 text.ll
|
||
# → Phase A _deps_pending 死锁(Scene3D ret_sha1=0ae 根因)。
|
||
same_cnt_b: int = 0
|
||
same_entry_b: GenTypes.StructEntry | t.CPtr = None
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if entry is not None and entry.Name is not None:
|
||
if string.strcmp(entry.Name, class_name) == 0:
|
||
same_cnt_b += 1
|
||
same_entry_b = entry
|
||
if same_cnt_b == 1:
|
||
return same_entry_b
|
||
return None
|
||
|
||
def find_struct_by_module(self, name: str, sha1: str) -> GenTypes.StructEntry | t.CPtr:
|
||
"""按类名 + SHA1 查找结构体,返回 StructEntry 或 None
|
||
|
||
SHA1 非 None 时:精确匹配 name+sha1,未找到返回 None(不回退,避免跨模块同名找错)
|
||
SHA1 为 None 时:回退到 find_struct(name) 按类名查找第一个
|
||
"""
|
||
if name is None or self.StructTable is None:
|
||
return None
|
||
# StructCount 合理性守卫(内存损坏导致巨大值 → 索引循环假挂起)
|
||
if self.StructCount < 0 or self.StructCount > 200000:
|
||
VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct_by_module)", "STRUCT")
|
||
stdlib.exit(1)
|
||
if sha1 is not None:
|
||
# 名索引快速路径:同名条目都位于 >= 首个同名下标处,故可从该下标起扫描
|
||
# name+sha1(等价原全表扫描"首个 name 且 sha1 命中")。
|
||
# 未命中/索引不可信 ⇒ start=0,走原全表扫描(保证正确性)。
|
||
start: int = 0
|
||
fri: int = self._struct_name_idx_find(name)
|
||
if fri >= 0:
|
||
start = fri
|
||
for i in range(start, self.StructCount):
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if entry is not None and entry.Name is not None:
|
||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。
|
||
if entry.Name[0] == name[0] and string.strcmp(entry.Name, name) == 0:
|
||
if entry.ModuleSha1 is not None:
|
||
# 同上:sha1 判等蕴含首字符相等,先比首字符再 strcmp。
|
||
if entry.ModuleSha1[0] == sha1[0] and string.strcmp(entry.ModuleSha1, sha1) == 0:
|
||
return entry
|
||
# SHA1 查找失败:不回退到 find_struct(会返回跨模块同名错误 entry)
|
||
# 返回 None 让调用方创建新类型,避免 Phase B 用错误类型短路
|
||
return None
|
||
# 无 SHA1:按类名查找第一个
|
||
return self.find_struct(name)
|
||
|
||
def reset_module_methods_emitted(self, sha1: str) -> int:
|
||
"""回滚指定模块注册类的 MethodsEmitted 标志(Phase A 重试前调用)
|
||
|
||
根因(追加87):Phase A 第 1 遍翻译类方法体时 _translate_oop_methods
|
||
先置 MethodsEmitted=1(防重入)再发射方法;方法体内跨模块调用因依赖
|
||
text.ll 未生成触发 trans._deps_pending → 整文件不写盘下遍重译。但
|
||
MethodsEmitted 存于 perm_pool 分配的 StructEntry,跨 mb.reset_to(mark2)
|
||
存活 → 第 2 遍 existing 分支被 _translate_oop_methods 的
|
||
MethodsEmitted==1 防重复检查拦截 → 类方法(含 __before_init__)永不
|
||
发射 → 链接 undefined symbol(模块级函数不受此标志保护所以正常,
|
||
AssignGenMixin text.ll 空 / HandlesTranslator 有模块函数缺类方法)。
|
||
按本文件 ModuleSha1 精确回滚,不影响其他已写盘文件的 MethodsEmitted。
|
||
"""
|
||
if sha1 is None or self.StructTable is None:
|
||
return 0
|
||
if self.StructCount < 0 or self.StructCount > 200000:
|
||
VLogger.error(f"StructCount 异常: {self.StructCount} (reset_module_methods_emitted)", "STRUCT")
|
||
return 0
|
||
reset_count: int = 0
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if entry is not None:
|
||
# 普通类按 ModuleSha1 匹配
|
||
if entry.ModuleSha1 is not None:
|
||
if string.strcmp(entry.ModuleSha1, sha1) == 0:
|
||
entry.MethodsEmitted = 0
|
||
reset_count += 1
|
||
continue
|
||
# 泛型特化类按触发文件 sha1 匹配:特化类 ModuleSha1 是模板 sha1
|
||
# (方法名带模板前缀),无法匹配当前作废文件。不重置则特化方法体
|
||
# 残留 MethodsEmitted=1,下遍不补译 → text.ll 只有 call 无 define
|
||
# → ld.lld undefined symbol(list[ast.ClassDef|t.CPtr].* 根因)
|
||
if entry.SpecTriggerSha1 is not None:
|
||
if string.strcmp(entry.SpecTriggerSha1, sha1) == 0:
|
||
entry.MethodsEmitted = 0
|
||
reset_count += 1
|
||
return reset_count
|
||
|
||
def find_struct_by_type(self, struct_ty: llvmlite.LLVMType | t.CPtr) -> GenTypes.StructEntry | t.CPtr:
|
||
"""按类型指针查找结构体(指针值比较),返回 StructEntry 或 None
|
||
|
||
注意: 不能用 `entry.Ty is struct_ty` 的 is 比较——TPV 编译 is 时会触发
|
||
translate_compare 的类型安全检查(is_ptr_type/TypePrint 递归打印),
|
||
对悬空 entry.Ty(1b 泛型特化类注册后被 reset 回收的 pool 指针)解引用崩溃。
|
||
改用 uint64 纯指针值比较,不解引用任何内容。
|
||
"""
|
||
if struct_ty is None or self.StructTable is None:
|
||
return None
|
||
# StructCount 合理性守卫(内存损坏导致巨大值 → 索引循环假挂起)
|
||
if self.StructCount < 0 or self.StructCount > 200000:
|
||
VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct_by_type)", "STRUCT")
|
||
stdlib.exit(1)
|
||
st_val: uint64 = uint64(struct_ty)
|
||
# Ty 索引整体停用判定(索引结构仍由 _struct_idx_add 维护,但不用于此处):
|
||
# 条目 Ty 可能被深拷贝改写为新指针(ClassGenDef),索引槽按"注册时的 Ty 值"
|
||
# 落位,改写后该条目的 live 键与槽位脱钩 ⇒ 索引命中返回的未必是"原线性扫描
|
||
# 首个 Ty 相等条目",命中路径与原语义不等价、且无法低成本保证等价。故此处
|
||
# 恢复原线性精确扫描(未命中/命中行为均与未加索引前逐字节一致)。
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if entry is not None and entry.Ty is not None:
|
||
if uint64(entry.Ty) == st_val:
|
||
return entry
|
||
return None
|
||
|
||
def struct_type_is_packed(self, struct_ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||
"""判断命名结构体是否 packed(@c.Attribute(t.attr.packed)),返回 1/0
|
||
|
||
先按类型指针身份匹配(find_struct_by_type,跨模块 deep copy 后仍
|
||
指向同一 entry.Ty)、miss 时按结构体名回退(等价类型实例),最终读
|
||
StructEntry.IsPacked(B5 在定义侧/stub 注入侧写入)。供 __sizeof__
|
||
尺寸与字段偏移计算:packed 结构体无字段填充,大小=字段尺寸之和。
|
||
"""
|
||
if struct_ty is None:
|
||
return 0
|
||
sname: str = llvmlite.get_struct_name(struct_ty)
|
||
if sname is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||
if entry is None:
|
||
entry = self.find_struct(sname)
|
||
if entry is None:
|
||
return 0
|
||
return entry.IsPacked
|
||
|
||
def lookup_field(self, struct_ty: llvmlite.LLVMType | t.CPtr,
|
||
field_name: str) -> GenTypes.FieldEntry | t.CPtr:
|
||
"""按结构体类型和字段名查找字段信息
|
||
|
||
两遍查找策略(跨模块同名类冲突的根本修复):
|
||
第一遍: 类型指针 is 身份比较(最高优先级,类型唯一确定 entry)
|
||
第二遍: 名称回退(is 未匹配时,按类名/strstr 匹配,字段未找到时 continue)
|
||
"""
|
||
if struct_ty is None or field_name is None or self.StructTable is None:
|
||
return None
|
||
|
||
# ============================================================
|
||
# 两遍查找策略(跨模块同名类冲突的根本修复):
|
||
#
|
||
# 第一遍: 用类型指针 is 身份比较(最高优先级)
|
||
# - is 匹配唯一确定 entry,字段未找到直接返回 None
|
||
# - 规避联合类型 == BUG,且不受遍历顺序影响
|
||
#
|
||
# 第二遍: 名称回退(is 未匹配到任何 entry 时才执行)
|
||
# - 用于未注册类型或 is 不可靠的场景
|
||
# - 名称匹配但字段未找到时 continue(继续检查其他同名 entry)
|
||
# ============================================================
|
||
|
||
# 第一遍: is 身份比较(用 uint64 指针值比较,避免 is 编译路径解引用悬空 Ty)
|
||
# find_struct_by_type 的语义 = 原扫描"首个 Ty 指针相等条目"(首注册优先);
|
||
# 其 Ty 索引判定已整体停用、恢复原线性扫描 ⇒ 选中的 entry 与原 is 身份扫描
|
||
# 完全一致。类型唯一确定 entry:命中后查字段,字段未找到直接返回 None。
|
||
lk_entry: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||
if lk_entry is not None:
|
||
for fi in range(lk_entry.FieldCount):
|
||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(lk_entry, fi)
|
||
if fe is not None and fe.Name is not None:
|
||
if string.strcmp(fe.Name, field_name) == 0:
|
||
return fe
|
||
return None
|
||
|
||
# 第二遍: 名称回退(is 未匹配到)
|
||
ty_name: str = self._extract_struct_name(struct_ty)
|
||
if ty_name is not None:
|
||
# 加强 SHA1 命名空间隔离:类型名形如 "3d4afd9cdffeba6d.Thread",
|
||
# 提取 16 位 hex 前缀与 entry.ModuleSha1 前 16 字符精确比较,
|
||
# 禁止 strstr 裸名跨模块抓同名类(sched.Thread 被 includes/
|
||
# vthreading.vp 同名 Thread 顶掉根因:strstr("sha1.Thread",
|
||
# "Thread") 命中 vthreading 的 entry → 其 arg@3 被当成
|
||
# sched.Thread.arg(应 4)→ th.func 被 th.arg=None 覆盖 →
|
||
# _thread_entry call 0 → #UD/#GP)。strncmp 不分配临时缓冲。
|
||
# 前缀判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:此前只判
|
||
# 首个 '.' 前恰 16 字节、不校验 hex 形态)。
|
||
ty_has_sha1_lf: int = 0
|
||
if GenApi.sha1_prefix_len(ty_name) != 0:
|
||
ty_has_sha1_lf = 1
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if entry is not None and entry.Name is not None:
|
||
# 类型名带 SHA1 前缀:必须与 entry.ModuleSha1 前 16 字符
|
||
# 精确一致,否则跳过(跨模块同名类命名空间隔离)
|
||
if ty_has_sha1_lf != 0:
|
||
if entry.ModuleSha1 is None \
|
||
or string.strlen(entry.ModuleSha1) < 16 \
|
||
or string.strncmp(entry.ModuleSha1, ty_name, 16) != 0:
|
||
continue
|
||
matched: int = 0
|
||
if string.strcmp(entry.Name, ty_name) == 0:
|
||
matched = 1
|
||
elif string.strstr(ty_name, entry.Name) is not None:
|
||
matched = 1
|
||
if matched != 0:
|
||
# 查找字段
|
||
for fi in range(entry.FieldCount):
|
||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
|
||
if fe is not None and fe.Name is not None:
|
||
if string.strcmp(fe.Name, field_name) == 0:
|
||
return fe
|
||
# 名称匹配但字段未找到,继续检查其他同名 entry(跨模块同名)
|
||
continue
|
||
# 所有条目都未匹配
|
||
return None
|
||
|
||
def _last_dot_outside_brackets(self, s: str) -> str:
|
||
"""返回字符串中最后一个位于 [...] 括号外的 '.' 指针,无则 None
|
||
|
||
泛型特化类名含下标(如 'numpy.ndarray[t.CDouble]'、
|
||
'list[f467978339422f32.AST|t.CPtr]'),下标内部也含 '.'(t.CDouble
|
||
的模块限定 / sha1.AST)。strrchr 直接找最后一个 '.' 会落到下标内部,
|
||
把模块名截错('numpy.ndarray[t' → sha1 映射表无法解析,NumpyTest
|
||
lookup_field_by_class FAIL 根因)。从末尾扫描并跟踪括号深度,
|
||
只认深度为 0 的 '.'。
|
||
"""
|
||
if s is None:
|
||
return None
|
||
n: sizet = string.strlen(s)
|
||
depth: int = 0
|
||
i: sizet = n
|
||
while i > 0:
|
||
i -= 1
|
||
ch: char = s[i]
|
||
if ch == ']':
|
||
depth += 1
|
||
elif ch == '[':
|
||
if depth > 0:
|
||
depth -= 1
|
||
elif ch == '.' and depth == 0:
|
||
return s + i
|
||
return None
|
||
|
||
def lookup_field_by_class(self, class_name: str,
|
||
field_name: str,
|
||
sha1: str = None,
|
||
node: ast.AST | t.CPtr = None,
|
||
from_imports: str = None) -> GenTypes.FieldEntry | t.CPtr:
|
||
"""按类名和字段名查找字段信息
|
||
|
||
优先用 SHA1 匹配(跨模块同名类区分),无 SHA1 时按类名查找第一个。
|
||
node: 出错位置 AST 节点(可选,供失败报错输出 Python 风格 traceback 帧;
|
||
传 None 时用 trace 栈兜底定位)
|
||
from_imports: from-import 映射("local:module" 格式),供模块限定名
|
||
的 import 别名(import X as Y → Y.Class)解析为模块 sha1
|
||
"""
|
||
if class_name is None or field_name is None:
|
||
return None
|
||
|
||
# 优先用 SHA1 匹配(跨模块同名类区分)
|
||
# 支持模块限定名:class_name 可能是 "HT.Translator",entry.Name 是 "Translator"
|
||
# 先精确匹配,失败后用 strrchr 提取类名部分回退匹配
|
||
if sha1 is not None:
|
||
sha1_matched: int = 0
|
||
# 计算类名回退部分("HT.Translator" → "Translator";含下标的
|
||
# "numpy.ndarray[t.CDouble]" → "ndarray[t.CDouble]")
|
||
bare_name: str = class_name
|
||
dot_pos_lfbc: str = self._last_dot_outside_brackets(class_name)
|
||
if dot_pos_lfbc is not None:
|
||
bare_name = dot_pos_lfbc + 1
|
||
if bare_name is None or bare_name[0] == '\0':
|
||
bare_name = class_name
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if entry is not None and entry.Name is not None:
|
||
# 精确匹配或类名回退匹配
|
||
name_match: int = 0
|
||
if string.strcmp(entry.Name, class_name) == 0:
|
||
name_match = 1
|
||
elif bare_name is not class_name and string.strcmp(entry.Name, bare_name) == 0:
|
||
name_match = 1
|
||
if name_match != 0:
|
||
if entry.ModuleSha1 is not None \
|
||
and string.strcmp(entry.ModuleSha1, sha1) == 0:
|
||
sha1_matched = 1
|
||
# SHA1 + 类名匹配,查找字段
|
||
for fi in range(entry.FieldCount):
|
||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
|
||
if fe is not None and fe.Name is not None \
|
||
and string.strcmp(fe.Name, field_name) == 0:
|
||
return fe
|
||
return None
|
||
|
||
# 模块限定名(module.Class,module 非 t/c 内置):按模块归属解析,
|
||
# 禁止裸名回退抓跨模块同名类(根因:VKernel sched.Thread 被 includes/
|
||
# vthreading.vp 同名 Thread 顶掉 → 'tid' 字段缺失)。
|
||
# 模块名经 StubMergerStore sha1 映射表解析为模块 sha1,再精确匹配
|
||
# name+sha1(vpi stub 注入 / 定义侧注册的裸名 entry)。
|
||
# 注意:泛型特化名含下标(numpy.ndarray[t.CDouble]),取点必须跳过
|
||
# 下标内部(_last_dot_outside_brackets),否则模块名截成
|
||
# 'numpy.ndarray[t' → sha1 无法解析(NumpyTest FAIL 根因)。
|
||
dot_pos_q: str = self._last_dot_outside_brackets(class_name)
|
||
if dot_pos_q is not None and dot_pos_q[1] != '\0':
|
||
bare_q: str = dot_pos_q + 1
|
||
mod_len_q: sizet = string.strlen(class_name) - string.strlen(bare_q) - 1
|
||
if mod_len_q > 0:
|
||
mod_buf_q: str = stdlib.malloc(mod_len_q + 1)
|
||
if mod_buf_q is not None:
|
||
string.strncpy(mod_buf_q, class_name, mod_len_q)
|
||
mod_buf_q[mod_len_q] = '\0'
|
||
if string.strcmp(mod_buf_q, "t") != 0 and string.strcmp(mod_buf_q, "c") != 0:
|
||
# 模块段是 16 位 hex = SHA1 字面量("{16hex}.Class" 类键形态,
|
||
# 泛型类型实参 _norm_type_arg_name 规范化产物,如
|
||
# "29fdb5e25dc949f4.Param"——GSList[Param] 特化方法体内
|
||
# `cur: T | t.CPtr = self.Head` 经 resolve_generic_type_name
|
||
# 替换 T 为实参串后 AnnotClassName 即该形态):模块段即模块
|
||
# SHA1,类已注册就证明该模块已编译,直接按 name+sha1 精确匹配,
|
||
# 不走 _resolve_module_sha1(store 键是模块名不是 sha1,查
|
||
# "29fdb5e25dc949f4" 恒 None → L956 fail-fast 误杀。本应找得到)。
|
||
# 对齐 find_struct L571-592 同款 hex 处理与 TypeGenResolve
|
||
# _is_sha1_prefixed 语义(含 sha1 前缀名的 Name/Attribute 注解
|
||
# 解析均跳过 import 检查)。泛型下标内点(GSList[sha.Param])
|
||
# 已被 _last_dot_outside_brackets 跳过,此处仅顶层 a.b 形态。
|
||
# [C10 收敛] 模块段恰为 16 位小写 hex = SHA1 字面量:
|
||
# 统一走 GenApi.is_sha1_hex16(该判定的唯一真源)。
|
||
mod_hex_ok_q: int = GenApi.is_sha1_hex16(mod_buf_q)
|
||
mod_sha1_q: str = None
|
||
if mod_hex_ok_q != 0:
|
||
# SHA1 前缀自身即"已登记"证据(与既有解析同语义):
|
||
# mod_sha1_q 别名 mod_buf_q(下方各返回路径统一 free 一次)
|
||
mod_sha1_q = mod_buf_q
|
||
else:
|
||
mod_sha1_q = self._resolve_module_sha1(mod_buf_q, from_imports)
|
||
if mod_sha1_q is not None:
|
||
mod_entry_q: GenTypes.StructEntry | t.CPtr = \
|
||
self.find_struct_by_module(bare_q, mod_sha1_q)
|
||
if mod_entry_q is not None:
|
||
# 依赖未就绪容忍(对齐裸名路径 L926):FieldCount==0 的
|
||
# 空类(注册不完整/源模块未编译)→ 返回 None 让上层标记
|
||
# _deps_pending 下遍重译,不在此 fatal(WIN32_FIND_DATAA
|
||
# 字段访问 cFileName 报 FieldCount=0 根因之一)。
|
||
if mod_entry_q.FieldCount == 0:
|
||
stdlib.free(mod_buf_q)
|
||
return None
|
||
for fi_q in range(mod_entry_q.FieldCount):
|
||
fe_q: GenTypes.FieldEntry | t.CPtr = \
|
||
self._get_field_entry(mod_entry_q, fi_q)
|
||
if fe_q is not None and fe_q.Name is not None:
|
||
if string.strcmp(fe_q.Name, field_name) == 0:
|
||
stdlib.free(mod_buf_q)
|
||
return fe_q
|
||
# REnum 变体字段别名回退(与裸名路径 L950-963 一致):
|
||
# 模块限定访问(llvmlite.LLVMType.Pointee)类命中后字段
|
||
# 未找到时,直接查变体别名(REnum 字段是 _p1/_p2 槽位,
|
||
# Pointee 是 Ptr 变体成员)。此前该回退只在裸名路径,
|
||
# 模块限定路径此处 fatal 先触发 → 永远走不到
|
||
# (CallGenDispatch.vp L1005 rk_pointee.Pointee 根因)。
|
||
rn_cls_q: str = mod_entry_q.Name
|
||
if rn_cls_q is None:
|
||
rn_cls_q = bare_q
|
||
rn_is_renum_q: int = \
|
||
self.Gen.ClassGenEnum._is_renum_class_name(bare_q)
|
||
if rn_is_renum_q == 0 and rn_cls_q is not None:
|
||
rn_is_renum_q = \
|
||
self.Gen.ClassGenEnum._is_renum_class_name(rn_cls_q)
|
||
rn_alias_q: int = -1
|
||
if rn_is_renum_q != 0:
|
||
rn_alias_q = self.Gen.ClassGenEnum._lookup_renum_field_alias(
|
||
rn_cls_q, field_name)
|
||
if rn_alias_q < 1 and bare_q is not rn_cls_q:
|
||
rn_alias_q = self.Gen.ClassGenEnum._lookup_renum_field_alias(
|
||
bare_q, field_name)
|
||
if rn_alias_q >= 1:
|
||
rn_fe_q: GenTypes.FieldEntry | t.CPtr = \
|
||
self._get_field_entry(mod_entry_q, rn_alias_q)
|
||
if rn_fe_q is not None and rn_fe_q.Ty is not None:
|
||
stdlib.free(mod_buf_q)
|
||
return rn_fe_q
|
||
# 类命中但字段未找到:可读化报错(traceback 格式)
|
||
self.Gen.TypeGenErr.fatal_error(node, f"lookup_field_by_class: 类 '{class_name}' 已按模块归属解析(ModuleSha1={mod_sha1_q}, FieldCount={mod_entry_q.FieldCount})但字段 '{field_name}' 未找到", "AttributeError")
|
||
stdlib.free(mod_buf_q)
|
||
return None
|
||
# 模块可解析但类未注册:fail-fast(暴露 vpi 注入/注册缺陷,
|
||
# 不再裸名跨库抓其他模块同名类)
|
||
self.Gen.TypeGenErr.fatal_error(node, f"lookup_field_by_class: 类 '{class_name}' 未找到(模块 '{mod_buf_q}' 可解析 sha1={mod_sha1_q},但无同名注册类;请检查 vpi stub 注入或编译顺序)", "AttributeError")
|
||
stdlib.free(mod_buf_q)
|
||
return None
|
||
# 模块名未登记:fail-fast(不再裸名跨库回退)
|
||
self.Gen.TypeGenErr.fatal_error(node, f"lookup_field_by_class: 类 '{class_name}' 的模块 '{mod_buf_q}' 无法解析(sha1 映射表未登记,请检查 import 与编译顺序)", "AttributeError")
|
||
stdlib.free(mod_buf_q)
|
||
return None
|
||
stdlib.free(mod_buf_q)
|
||
|
||
# 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个
|
||
entry = self.find_struct(class_name)
|
||
if entry is None:
|
||
# 诊断:目标类未注册(仿 Python traceback 格式,立即终止编译)
|
||
self.Gen.TypeGenErr.fatal_error(node, f"lookup_field_by_class: 类 '{class_name}' 未注册 (struct_count={self.StructCount})", "AttributeError")
|
||
return None
|
||
# 依赖未就绪容忍:调用方期望特定模块(sha1 非 None)但裸名回退命中的是
|
||
# FieldCount==0 的 stub 空类(vpi 注入不含字段 / 定义侧模块未编译注册)→
|
||
# 返回 None 让上层标记 _deps_pending 下遍重译(源模块编译后真实定义覆盖
|
||
# stub)。SECURITY_ATTRIBUTES FieldCount=0 根因(subprocess.vp sa.nLength)。
|
||
if entry.FieldCount == 0 and sha1 is not None:
|
||
return None
|
||
# 先查找字段(诊断/error 之前返回,否则 error 立即退出导致查找永远失败)
|
||
for fi in range(entry.FieldCount):
|
||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
|
||
if fe is not None and fe.Name is not None:
|
||
if string.strcmp(fe.Name, field_name) == 0:
|
||
return fe
|
||
# REnum 变体字段别名回退:REnum 类(如 LLVMType)的变体内部字段
|
||
# (Ptr.Pointee 等)不是普通字段,而是 _p1/_p2 槽位别名。此前在调用方
|
||
# (ExprGenMixin 按需过滤 L5046/L5500)回退,但本函数字段未找到的
|
||
# VLogger.error 立即 sys.exit(1),调用方回退永远执行不到("LLVMType 已注册
|
||
# 但字段 Pointee 未找到"四次依旧的根因)。此处直接查变体别名,所有调用方
|
||
# 统一受益。注意:_lookup_renum_field_alias 用 class_name 指针值比较,
|
||
# 必须传 entry.Name(register_struct 复制 + _update_renum_alias_class_ptr
|
||
# 统一后的池指针),传调用方 class_name(AnnotClassName 等不同指针)
|
||
# 恒不匹配 → 返回 -1 → 回退失效。
|
||
rn_cls_l: str = entry.Name
|
||
if rn_cls_l is None:
|
||
rn_cls_l = class_name
|
||
rn_is_renum_l: int = self.Gen.ClassGenEnum._is_renum_class_name(class_name)
|
||
if rn_is_renum_l == 0 and rn_cls_l is not None:
|
||
# 类名可能是模块限定名("mod.LLVMType"),REnum 表存裸名,双保险
|
||
rn_is_renum_l = self.Gen.ClassGenEnum._is_renum_class_name(rn_cls_l)
|
||
rn_alias_l: int = -1
|
||
if rn_is_renum_l != 0:
|
||
rn_alias_l = self.Gen.ClassGenEnum._lookup_renum_field_alias(
|
||
rn_cls_l, field_name)
|
||
if rn_alias_l < 1 and class_name is not rn_cls_l:
|
||
# 双保险:别名表可能仍持有旧 AST 指针(_update_renum_alias_class_ptr
|
||
# 未执行/多模块重复注册场景),用调用方 class_name 再试一次
|
||
rn_alias_l = self.Gen.ClassGenEnum._lookup_renum_field_alias(
|
||
class_name, field_name)
|
||
if rn_alias_l >= 1:
|
||
rn_fe_l: GenTypes.FieldEntry | t.CPtr = \
|
||
self.Gen.StructGen._get_field_entry(entry, rn_alias_l)
|
||
if rn_fe_l is not None and rn_fe_l.Ty is not None:
|
||
return rn_fe_l
|
||
# 诊断:找到结构体但字段确实未找到
|
||
# 拼接注册字段列表(一行逗号分隔,供报错一眼定位)
|
||
fb_flds_lst: str = stdlib.malloc(1024)
|
||
if fb_flds_lst is not None:
|
||
fb_flds_lst[0] = '\0'
|
||
for fi in range(entry.FieldCount):
|
||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
|
||
if fe is not None and fe.Name is not None:
|
||
if string.strlen(fb_flds_lst) > 0 and string.strlen(fb_flds_lst) + 3 < 1024:
|
||
string.strcat(fb_flds_lst, ", ")
|
||
if string.strlen(fb_flds_lst) + string.strlen(fe.Name) < 1024:
|
||
string.strcat(fb_flds_lst, fe.Name)
|
||
err: str
|
||
if rn_is_renum_l != 0:
|
||
# 报错友好化:REnum 类直接说明是"变体字段别名查找失败",并附
|
||
# entry.Name 与别名槽位结果,一眼可辨"别名未注册"(槽位=-1)
|
||
# 还是"class_name 指针未统一"(已注册但传错指针查不到)
|
||
err = f"lookup_field_by_class: 类 '{class_name}' 是 REnum 类(FieldCount={entry.FieldCount}),变体字段 '{field_name}' 别名查找失败(entry.Name='{rn_cls_l}',别名槽位={rn_alias_l},请检查该变体字段是否已注册别名,或 class_name 指针是否统一)"
|
||
else:
|
||
# 可读化:附 entry.Name/ModuleSha1(跨模块同名定位)+ 注册字段
|
||
# 列表 + 期望 sha1(调用方)→ 一眼分辨"跨模块同名类误匹配"
|
||
# vs "vpi stub 注入字段不全" vs "类定义与访问处不一致"
|
||
sha1_txt: str = "?"
|
||
if entry.ModuleSha1 is not None:
|
||
sha1_txt = entry.ModuleSha1
|
||
want_sha1_txt: str = "?"
|
||
if sha1 is not None:
|
||
want_sha1_txt = sha1
|
||
fld_lst_txt: str = "(无)"
|
||
if fb_flds_lst is not None and string.strlen(fb_flds_lst) > 0:
|
||
fld_lst_txt = fb_flds_lst
|
||
err = f"lookup_field_by_class: 类 '{class_name}' 已注册 (FieldCount={entry.FieldCount}, "\
|
||
f"entry.Name='{entry.Name}', ModuleSha1={sha1_txt}, 调用方期望 sha1={want_sha1_txt}) "\
|
||
f"但字段 '{field_name}' 未找到;注册字段: [{fld_lst_txt}]。可能原因: 跨模块同名类"\
|
||
f"被误匹配 / vpi stub 注入字段不全 / 类定义与访问处不一致"
|
||
# 仿 Python traceback 格式输出(Traceback 帧 + 文件:行号 + 错误类型),
|
||
# 立即终止编译(与 VLogger.error 同语义,格式对齐 fatal_error)
|
||
self.Gen.TypeGenErr.fatal_error(node, err, "AttributeError")
|
||
if fb_flds_lst is not None:
|
||
stdlib.free(fb_flds_lst)
|
||
# 诊断:找到结构体但字段未找到
|
||
return None
|
||
|
||
def get_struct_type(self, class_name: str) -> llvmlite.LLVMType | t.CPtr:
|
||
"""按类名获取结构体的 LLVM 类型"""
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(class_name)
|
||
if entry is not None:
|
||
return entry.Ty
|
||
return None
|
||
|
||
def get_field_by_index(self, class_name: str,
|
||
idx: int) -> GenTypes.FieldEntry | t.CPtr:
|
||
"""按类名和字段索引获取字段信息"""
|
||
if class_name is None:
|
||
return None
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(class_name)
|
||
if entry is None:
|
||
return None
|
||
if idx < 0 or idx >= entry.FieldCount:
|
||
return None
|
||
return self._get_field_entry(entry, idx)
|
||
|
||
def struct_has_defaults(self, class_name: str) -> int:
|
||
"""检查结构体是否有任何带默认值的字段,返回 1=有 / 0=无"""
|
||
if class_name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(class_name)
|
||
if entry is None:
|
||
return 0
|
||
for fi in range(entry.FieldCount):
|
||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
|
||
if fe is not None and fe.DefaultVal is not None:
|
||
return 1
|
||
return 0
|
||
|
||
def get_struct_type_from_value(self, val: llvmlite.Value | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
|
||
"""从 Value 的类型推断结构体类型
|
||
|
||
递归解 Ptr:支持 X|t.CPtr 联合简化产生的 Ptr(Ptr(Struct)) 对象指针
|
||
(如运算符重载中间结果 a*2.0 的 Vec3**),否则 try_operator_overload
|
||
重载识别失败回退原生运算,对指针生成非法 sub 指令(llc: invalid
|
||
operand type for instruction)。
|
||
"""
|
||
if val is None or val.Ty is None:
|
||
return None
|
||
ty: llvmlite.LLVMType | t.CPtr = val.Ty
|
||
while ty is not None:
|
||
# 同类编译 bug(见 TypeGenResolve L1463 注释):case body 直接
|
||
# 更新 while 条件变量(ty = pointee)时自举编译出的 match 把赋值
|
||
# 写到 subject 快照槽,while 条件读旧槽 → 死循环。改法:case
|
||
# 绑定到局部变量,match 外更新循环变量。
|
||
nxt_ty: llvmlite.LLVMType | t.CPtr = None
|
||
match ty:
|
||
case llvmlite.LLVMType.Ptr(pointee):
|
||
nxt_ty = pointee
|
||
case llvmlite.LLVMType.Struct(fields, fcount, name):
|
||
return ty
|
||
case _:
|
||
return None
|
||
ty = nxt_ty
|
||
return None
|
||
|
||
def mark_as_union(self, name: str) -> int:
|
||
"""标记已注册的结构体为联合体,返回 1=成功 / 0=失败"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.IsUnion = 1
|
||
return 1
|
||
|
||
def is_union_by_name(self, name: str) -> int:
|
||
"""按类名检查是否为联合体,返回 1=是 / 0=否"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.IsUnion
|
||
|
||
def is_union_by_type(self, struct_ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||
"""按类型指针检查是否为联合体,返回 1=是 / 0=否
|
||
|
||
先尝试类型指针 == 比较,失败时回退到名称匹配
|
||
"""
|
||
if struct_ty is None or self.StructTable is None:
|
||
return 0
|
||
ty_name: str = self._extract_struct_name(struct_ty)
|
||
st_iu: uint64 = uint64(struct_ty)
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||
if entry is not None and entry.Ty is not None:
|
||
matched: int = 0
|
||
if uint64(entry.Ty) == st_iu:
|
||
matched = 1
|
||
elif ty_name is not None and entry.Name is not None:
|
||
if string.strcmp(entry.Name, ty_name) == 0:
|
||
matched = 1
|
||
elif string.strstr(ty_name, entry.Name) is not None:
|
||
matched = 1
|
||
if matched != 0:
|
||
return entry.IsUnion
|
||
return 0
|
||
|
||
def mark_as_oop(self, name: str) -> int:
|
||
"""标记已注册的结构体为 OOP,返回 1=成功 / 0=失败"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.IsOOP = 1
|
||
return 1
|
||
|
||
def is_oop_by_name(self, name: str) -> int:
|
||
"""按类名检查是否为 OOP 结构体,返回 1=是 / 0=否"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.IsOOP
|
||
|
||
def mark_has_init(self, name: str) -> int:
|
||
"""标记结构体拥有 __init__ 方法,返回 1=成功 / 0=失败"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.HasInit = 1
|
||
return 1
|
||
|
||
def has_init_by_name(self, name: str) -> int:
|
||
"""按类名检查是否有 __init__ 方法,返回 1=有 / 0=无"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.HasInit
|
||
|
||
def mark_has_new(self, name: str) -> int:
|
||
"""标记结构体拥有 __new__ 方法,返回 1=成功 / 0=失败"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.HasNew = 1
|
||
return 1
|
||
|
||
def has_new_by_name(self, name: str) -> int:
|
||
"""按类名检查是否有 __new__ 方法,返回 1=有 / 0=无"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.HasNew
|
||
|
||
def get_class_name_by_type(self, pool: rowptr[memhub.MemBuddy],
|
||
struct_ty: llvmlite.LLVMType | t.CPtr) -> str:
|
||
"""按类型指针查找类名,返回类名字符串或 None"""
|
||
if struct_ty is None or self.StructTable is None:
|
||
return None
|
||
# find_struct_by_type 的语义 = 原扫描"首个 Ty 指针相等条目"(首注册优先),
|
||
# 返回其 Name;其 Ty 索引判定已整体停用、恢复原线性扫描 ⇒ 逐字节一致。
|
||
fast_cn: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||
if fast_cn is not None:
|
||
return fast_cn.Name
|
||
# 回退: 类型指针比较失败时,遍历所有注册的结构体按名称匹配
|
||
# 提取 struct_ty 的 Name 字段(可能是 "SHA1.ClassName" 格式)
|
||
ty_name: str = self._extract_struct_name(struct_ty)
|
||
if ty_name is not None:
|
||
for j in range(self.StructCount):
|
||
entry2: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(j)
|
||
if entry2 is not None and entry2.Name is not None:
|
||
# 构造 "SHA1.ClassName" 格式进行比较
|
||
entry2_sha1: str = entry2.ModuleSha1
|
||
if entry2_sha1 is not None:
|
||
full_nm: str = pool.alloc(128)
|
||
if full_nm is not None:
|
||
viperlib.snprintf(full_nm, 128, "%s.%s", entry2_sha1, entry2.Name)
|
||
if string.strcmp(full_nm, ty_name) == 0:
|
||
return entry2.Name
|
||
# 也直接比较类名(struct_ty 的 Name 可能无 SHA1 前缀)
|
||
if string.strcmp(entry2.Name, ty_name) == 0:
|
||
return entry2.Name
|
||
# 最后回退: 后缀匹配("SHA1.ClassName" 尾部)——比 strstr 子串
|
||
# 匹配安全:特化名 "SHA1.GSList[Param]" 会被基础名 "GSList" 错误
|
||
# 命中(子串),导致链式方法调用 class_name 解析错(如
|
||
# func.Params.append → GSList.append 查找失败 → 兄弟 Mixin 兜底
|
||
# 误匹配 LinkedNode.append,this 前移 sizeof(LinkedNode) 写穿
|
||
# 容器前 48 字节,LLvmLiteTest Test 3 崩溃根因)。
|
||
if entry2.Name is not None and string.strlen(entry2.Name) > 0:
|
||
_tlen_cn: sizet = string.strlen(ty_name)
|
||
_nlen_cn: sizet = string.strlen(entry2.Name)
|
||
if _tlen_cn > _nlen_cn and ty_name[_tlen_cn - _nlen_cn - 1] == '.':
|
||
_tail_ok_cn: int = 1
|
||
_ti_cn: sizet
|
||
for _ti_cn in range(_nlen_cn):
|
||
if ty_name[_tlen_cn - _nlen_cn + _ti_cn] != entry2.Name[_ti_cn]:
|
||
_tail_ok_cn = 0
|
||
break
|
||
if _tail_ok_cn == 1:
|
||
return entry2.Name
|
||
return None
|
||
|
||
def _extract_struct_name(self, ty: llvmlite.LLVMType | t.CPtr) -> str:
|
||
"""从 LLVMType 提取 Struct 变体的 Name 字段,返回 None 非 Struct"""
|
||
if ty is None:
|
||
return None
|
||
match ty:
|
||
case llvmlite.LLVMType.Struct(fields, fcount, name):
|
||
return name
|
||
case _:
|
||
return None
|
||
|
||
def mark_has_vtable(self, name: str) -> int:
|
||
"""标记结构体拥有虚表,返回 1=成功 / 0=失败"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.HasVTable = 1
|
||
return 1
|
||
|
||
def has_vtable_by_name(self, name: str) -> int:
|
||
"""按类名检查是否有虚表,返回 1=有 / 0=无"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.HasVTable
|
||
|
||
def mark_novtable(self, name: str) -> int:
|
||
"""标记结构体为 NoVTable,返回 1=成功 / 0=失败"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.IsNoVTable = 1
|
||
return 1
|
||
|
||
def is_novtable_by_name(self, name: str) -> int:
|
||
"""按类名检查是否为 NoVTable,返回 1=是 / 0=否"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.IsNoVTable
|
||
|
||
def set_parent_name(self, name: str, parent_name: str) -> int:
|
||
"""设置结构体的父类名,返回 1=成功 / 0=失败"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.ParentName = parent_name
|
||
return 1
|
||
|
||
def set_parent_names(self, pool: rowptr[memhub.MemBuddy],
|
||
name: str, names_ptr: sizet | t.CPtr,
|
||
count: int) -> int:
|
||
"""设置结构体的父类名数组(永久池深拷贝,跨文件永久存活)"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
if names_ptr is None or count <= 0:
|
||
entry.ParentNames = None
|
||
entry.ParentCount = 0
|
||
return 1
|
||
if count > PARENT_MAX:
|
||
count = PARENT_MAX
|
||
pn_pool: rowptr[memhub.MemBuddy] = pool
|
||
if self.StdlibAlloc != 0:
|
||
pn_pool = CompilerFactory.factory.PermPool
|
||
if pn_pool is None:
|
||
pn_pool = pool
|
||
arr: sizet | t.CPtr = pn_pool.alloc(8 * count)
|
||
if arr is None:
|
||
return 0
|
||
for i in range(count):
|
||
addr: sizet = names_ptr[i]
|
||
arr[i] = 0
|
||
if addr != 0:
|
||
src: str = (str | t.CPtr)(t.CVoid(addr, t.CPtr))
|
||
if src is not None:
|
||
slen: sizet = string.strlen(src)
|
||
dup: str = pn_pool.alloc(slen + 1)
|
||
if dup is not None:
|
||
string.strcpy(dup, src)
|
||
arr[i] = sizet(dup)
|
||
entry.ParentNames = arr
|
||
entry.ParentCount = count
|
||
return 1
|
||
|
||
def get_parent_count(self, name: str) -> int:
|
||
"""获取父类数量(0=无父类,1=单继承,>1=多继承)"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.ParentCount
|
||
|
||
def get_parent_name_at(self, name: str, idx: int) -> str:
|
||
"""获取第 idx 个父类名,越界返回 None"""
|
||
if name is None or idx < 0:
|
||
return None
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None or entry.ParentNames is None:
|
||
return None
|
||
if idx >= entry.ParentCount:
|
||
return None
|
||
parr: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(entry.ParentNames, t.CPtr))
|
||
addr: sizet = parr[idx]
|
||
if addr == 0:
|
||
return None
|
||
return (str | t.CPtr)(t.CVoid(addr, t.CPtr))
|
||
|
||
def get_mro_chain(self, pool: rowptr[memhub.MemBuddy],
|
||
name: str) -> sizet | t.CPtr:
|
||
"""MRO 线性化类名数组(深度优先 + 声明顺序 + 去重,含自身)"""
|
||
if pool is None or name is None:
|
||
return None
|
||
arr: sizet | t.CPtr = pool.alloc(8 * (MRO_MAX + 1))
|
||
if arr is None:
|
||
return None
|
||
stack: sizet | t.CPtr = pool.alloc(8 * MRO_MAX)
|
||
if stack is None:
|
||
return None
|
||
visited: sizet | t.CPtr = pool.alloc(8 * MRO_MAX)
|
||
if visited is None:
|
||
return None
|
||
stack_count: int = 0
|
||
out_count: int = 0
|
||
visit_count: int = 0
|
||
# 初始压入自身
|
||
stack[0] = sizet(name)
|
||
stack_count = 1
|
||
while stack_count > 0:
|
||
stack_count -= 1
|
||
cur_addr: sizet = stack[stack_count]
|
||
cur: str = (str | t.CPtr)(t.CVoid(cur_addr, t.CPtr))
|
||
if cur is None:
|
||
continue
|
||
# 已访问检查(线性搜索)
|
||
dup: int = 0
|
||
for vi in range(visit_count):
|
||
va: sizet = visited[vi]
|
||
if va != 0:
|
||
vn: str = (str | t.CPtr)(t.CVoid(va, t.CPtr))
|
||
if vn is not None and string.strcmp(vn, cur) == 0:
|
||
dup = 1
|
||
break
|
||
if dup != 0:
|
||
continue
|
||
# 加入结果与已访问集合
|
||
if out_count < MRO_MAX:
|
||
arr[out_count] = sizet(cur)
|
||
out_count += 1
|
||
if visit_count < MRO_MAX:
|
||
visited[visit_count] = sizet(cur)
|
||
visit_count += 1
|
||
# 父类逆序压栈(正向 range 计算逆序索引,保证声明顺序弹出)
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(cur)
|
||
if entry is not None and entry.ParentCount > 0 and entry.ParentNames is not None:
|
||
pc: int = entry.ParentCount
|
||
parr: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(entry.ParentNames, t.CPtr))
|
||
for pi2 in range(pc):
|
||
pi: int = pc - 1 - pi2
|
||
if stack_count < MRO_MAX:
|
||
stack[stack_count] = parr[pi]
|
||
stack_count += 1
|
||
# 末尾 0 结束
|
||
arr[out_count] = 0
|
||
return arr
|
||
|
||
def get_parent_name(self, name: str) -> str:
|
||
"""获取结构体的父类名,返回 None=无父类"""
|
||
if name is None:
|
||
return None
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return None
|
||
return entry.ParentName
|
||
|
||
def set_vtable_methods(self, name: str, methods: str, count: int) -> int:
|
||
"""设置虚表方法名数组,返回 1=成功 / 0=失败"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.VTableMethods = methods
|
||
entry.VTableMethodCount = count
|
||
return 1
|
||
|
||
def get_vtable_method_index(self, name: str, method_name: str) -> int:
|
||
"""获取方法在虚表中的索引,返回 -1=未找到"""
|
||
if name is None or method_name is None:
|
||
return -1
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None or entry.VTableMethods is None:
|
||
return -1
|
||
# VTableMethods 字段声明为 char*,但实际存储的是 sizet 数组
|
||
# 必须转换为 sizet* 才能正确读取 8 字节指针值
|
||
methods_arr: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(entry.VTableMethods, t.CPtr))
|
||
for i in range(entry.VTableMethodCount):
|
||
mname_addr: sizet = methods_arr[i]
|
||
if mname_addr == 0:
|
||
continue
|
||
mname: str = (str | t.CPtr)(t.CVoid(mname_addr, t.CPtr))
|
||
if mname is not None and string.strcmp(mname, method_name) == 0:
|
||
return i
|
||
return -1
|
||
|
||
def get_vtable_method_count(self, name: str) -> int:
|
||
"""获取虚表方法数量"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.VTableMethodCount
|
||
|
||
def get_vtable_method_name(self, name: str, idx: int) -> str:
|
||
"""获取虚表中第 idx 个方法名,返回 None=越界"""
|
||
if name is None or idx < 0:
|
||
return None
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None or entry.VTableMethods is None:
|
||
return None
|
||
if idx >= entry.VTableMethodCount:
|
||
return None
|
||
methods_arr: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(entry.VTableMethods, t.CPtr))
|
||
mname_addr: sizet = methods_arr[idx]
|
||
if mname_addr == 0:
|
||
return None
|
||
return (str | t.CPtr)(t.CVoid(mname_addr, t.CPtr))
|
||
|
||
def get_field_count(self, name: str) -> int:
|
||
"""获取结构体字段数量,返回 -1=未找到"""
|
||
if name is None:
|
||
return -1
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct(name)
|
||
if entry is None:
|
||
return -1
|
||
return entry.FieldCount
|
||
|
||
def ensure_struct_def_in_module(self, pool: rowptr[memhub.MemBuddy],
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
ty: llvmlite.LLVMType | t.CPtr):
|
||
"""确保跨模块结构体的完整定义在当前模块中可用(供 alloca 使用)
|
||
|
||
处理两种情况:
|
||
1. Struct 值类型:需要完整定义(供 alloca 分配空间)
|
||
2. Ptr(Struct) 指针类型:需要 pointee 至少有 opaque 声明
|
||
"""
|
||
if ty is None or mod is None or pool is None:
|
||
return
|
||
# 指针类型:确保 pointee 至少有 opaque 声明
|
||
match ty:
|
||
case llvmlite.LLVMType.Ptr(pointee):
|
||
llvmlite.module_ensure_opaque_for_type(mod, pool, ty)
|
||
# 如果 pointee 是 Struct,递归复制完整定义
|
||
# (GEP 访问字段需要 sized 类型,仅 opaque 会导致 llc 报错)
|
||
if pointee is not None:
|
||
self.ensure_struct_def_in_module(pool, mod, pointee)
|
||
return
|
||
case _:
|
||
pass
|
||
# 获取结构体名称(非 Struct 类型返回 None)
|
||
sname: str = llvmlite.get_struct_name(ty)
|
||
if sname is None:
|
||
return
|
||
# 检查是否为跨模块引用(名称包含 '.')
|
||
if string.strchr(sname, 46) is None:
|
||
return # 非跨模块引用,无需处理
|
||
# 提取类名(第一个 '.' 之后的部分)
|
||
# 必须用 strchr 而非 strrchr:泛型特化名如 "list[ast.AST|t.CPtr]"
|
||
# 内部含 '.'(ast.AST),strrchr 会错误地把类名解析为 "AST|t.CPtr]"
|
||
dot_pos: str = string.strchr(sname, '.')
|
||
if dot_pos is None:
|
||
return
|
||
class_name: str = dot_pos + 1
|
||
if class_name is None or class_name[0] == '\0':
|
||
return
|
||
# 提取 SHA1(sname 到 dot_pos 之间,用 strlen 避免指针减法)
|
||
total_len: sizet = string.strlen(sname)
|
||
class_name_len: sizet = string.strlen(class_name)
|
||
sha1_len: sizet = total_len - class_name_len - 1
|
||
sha1_buf: str = pool.alloc(32)
|
||
if sha1_buf is None:
|
||
return
|
||
# 32 字节缓冲:strncpy 不截断,前缀超过 31 字符会越界写 + 手动 NUL 再越界
|
||
sha1_cp: sizet = sha1_len
|
||
if sha1_cp > 31:
|
||
sha1_cp = 31
|
||
string.strncpy(sha1_buf, sname, sha1_cp)
|
||
sha1_buf[sha1_cp] = '\0'
|
||
# 按类名 + SHA1 查找结构体定义
|
||
entry: GenTypes.StructEntry | t.CPtr = self.find_struct_by_module(class_name, sha1_buf)
|
||
if entry is None:
|
||
# 回退到按类名查找(可能找到不同模块的同名类,但总比没有好)
|
||
entry = self.find_struct(class_name)
|
||
if entry is None:
|
||
return
|
||
if entry.Ty is None:
|
||
return
|
||
# 将完整定义添加到当前模块(会替换已存在的 opaque 声明)
|
||
llvmlite.module_add_named_type(mod, pool, entry.Ty)
|
||
# B5:跨模块注入的 packed 类必须同步 packed 标记(<{...}>),否则引用
|
||
# 模块按非 packed 布局 GEP,与定义侧 packed 布局错位(gdt/elf/fat32
|
||
# 二进制格式字段错读;合并 IR 中 idt_entry/gdt_entry 退化 {…} 根因)。
|
||
if entry.IsPacked != 0:
|
||
llvmlite.module_set_named_type_packed(mod, pool, entry.Ty, 1)
|
||
# 递归扫描字段类型,确保值类型子结构体(如 WIN32_FIND_DATAA 中的 FILETIME)
|
||
# 也有完整定义。指针类型字段只需 opaque 声明(已由 module_ensure_opaque_for_type 处理)。
|
||
self._ensure_field_types_in_module(pool, mod, entry.Ty)
|
||
|
||
def _ensure_field_types_in_module(self, pool: rowptr[memhub.MemBuddy],
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
ty: llvmlite.LLVMType | t.CPtr):
|
||
"""递归扫描类型树,确保值类型子结构体有完整定义"""
|
||
if ty is None or mod is None or pool is None:
|
||
return
|
||
# 嵌套 match 遍历类型树
|
||
match ty:
|
||
case llvmlite.LLVMType.Struct(fields, fcount, sname):
|
||
# 遍历字段链表
|
||
if fields is not None:
|
||
fld: llvmlite.ParamNode | t.CPtr = fields
|
||
cnt_ef: int = 0
|
||
while fld is not None:
|
||
# 步数守卫:字段链表成环(内存损坏)导致无限遍历挂起
|
||
# (G3 LinkedNode.prepend 挂起候选),超限立即报错终止
|
||
cnt_ef += 1
|
||
if cnt_ef > 100000:
|
||
VLogger.error(
|
||
"字段链表疑似成环(_ensure_field_types_in_module),强制终止",
|
||
"STRUCT")
|
||
stdlib.exit(1)
|
||
if fld.Ty is not None:
|
||
# 值类型子结构体(如 FILETIME)需要完整定义
|
||
match fld.Ty:
|
||
case llvmlite.LLVMType.Struct(f2, fc2, n2):
|
||
self.ensure_struct_def_in_module(pool, mod, fld.Ty)
|
||
case _:
|
||
pass
|
||
self._ensure_field_types_in_module(pool, mod, fld.Ty)
|
||
fld = fld.Next
|
||
case llvmlite.LLVMType.Ptr(pointee):
|
||
# 指针类型:pointee 只需 opaque 声明,不需要完整定义
|
||
# 但如果 pointee 是 Struct,确保 opaque 声明存在
|
||
if pointee is not None:
|
||
llvmlite.module_ensure_opaque_for_type(mod, pool, ty)
|
||
case llvmlite.LLVMType.Array(elem_ty, acount):
|
||
# 数组类型:元素如果是值类型结构体,需要完整定义
|
||
# (如 zhuff_decode_tree = {MemManager*, [576 x zhuff_decode_node],
|
||
# i32, i32}——Array 元素 node 只递归字段不发射定义 → llc "use of
|
||
# undefined type named '...zhuff_decode_node'",ZlibTest 根因)
|
||
if elem_ty is not None:
|
||
match elem_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
self.ensure_struct_def_in_module(pool, mod, elem_ty)
|
||
case _:
|
||
pass
|
||
self._ensure_field_types_in_module(pool, mod, elem_ty)
|
||
case _:
|
||
pass
|
||
|
||
def get_field_name_ptr(self, fe: GenTypes.FieldEntry | t.CPtr) -> str:
|
||
"""获取 FieldEntry 的字段名,返回 None=未设置"""
|
||
if fe is None:
|
||
return None
|
||
return fe.Name
|
||
|
||
def get_field_type_ptr(self, fe: GenTypes.FieldEntry | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
|
||
"""获取 FieldEntry 的字段类型,返回 None=未设置"""
|
||
if fe is None:
|
||
return None
|
||
return fe.Ty
|
||
|
||
def get_field_default_ptr(self, fe: GenTypes.FieldEntry | t.CPtr) -> ast.AST | t.CPtr:
|
||
"""获取 FieldEntry 的默认值 AST 节点,返回 None=无默认值"""
|
||
if fe is None:
|
||
return None
|
||
return fe.DefaultVal
|
||
|
||
def get_field_annot_class_name(self, fe: GenTypes.FieldEntry | t.CPtr) -> str:
|
||
"""获取 FieldEntry 的原始类型注解类名(Spec.SourceName),返回 None=未设置
|
||
|
||
[S4] 原 FieldEntry.AnnotClassName 字段已删除,注解名承载在 Spec.SourceName
|
||
(由 add_field 经 SpecWithSource 写入)。
|
||
"""
|
||
if fe is None:
|
||
return None
|
||
return TSpec.SpecSourceName(fe.Spec)
|