- 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 属性访问失败)。
2308 lines
126 KiB
Plaintext
2308 lines
126 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
|
||
#
|
||
# 职责:struct 表注册与查找(名/类型索引)、字段查找、跨模块归属匹配。
|
||
# @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 字段,
|
||
# 上限取 64 会导致第 9+ 父类字段丢失 → __init__ 写属性失败。
|
||
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 - 结构体注册表/字段查找/可见性
|
||
# ============================================================
|
||
novirtual class StructGenMixin:
|
||
"""结构体注册与查找 Mixin(状态从 HandlesStruct 全局变量迁入)"""
|
||
|
||
# --- 全局注册表(原 _struct_table/_struct_count) ---
|
||
Gen: Generator.Generator*
|
||
StructTable: GenTypes.StructEntry* # STRUCT_MAX 槽位数组
|
||
StructCount: int
|
||
|
||
# --- OOM 回收分配模式(原 _g_stdlib_alloc) ---
|
||
StdlibAlloc: int
|
||
|
||
# --- 跨模块命名空间隔离(原 _visible_structs/_strict_visibility) ---
|
||
VisibleStructs: hashtable.HashTable*
|
||
StrictVisibility: int
|
||
|
||
# --- 各模块"声明的类名"集合(键 "{模块SHA1}.{类名}")---
|
||
# 与 StructTable 的注册状态**无关**:类定义入口处即登记(ClassGenDef:43 之后),
|
||
# 因此"类正在定义中、struct 还没有条目"时也能答出该裸类名的归属模块。
|
||
# 用途:泛型类型实参命名规范化(TypeGenResolve._norm_type_arg_name 规则 5.5)
|
||
# 判定裸类名属于哪个模块 —— 自引用字段(如 includes/ast/base.vp
|
||
# `AST.children: list[rawptr[AST]]`)解析时本类尚未 register_struct,只按
|
||
# 注册表查会把实参留在裸名,与同一个类在构造调用/别的模块处的带 SHA1 形式
|
||
# 分裂成两个特化名(现场:base.vp:357 CastError,源/目标只差实参类名前缀)。
|
||
ModuleClasses: hashtable.HashTable*
|
||
|
||
# --- **正在定义类**所属模块的 SHA1(mark_module_class 同步记录)---
|
||
# 为什么不复用 TypeGenBase.CurrentModuleSha1:A-pre/StubMerger 等路径只设了
|
||
# Translator.ModuleSha1、没设 CurrentModuleSha1(或留着上一文件的旧值),
|
||
# 而类定义入口拿到的 trans.ModuleSha1 在任何路径上都是权威的 ⇒ 以它为"当前
|
||
# 定义模块"的唯一真源。
|
||
CurDefModuleSha1: str
|
||
|
||
# --- **正在定义类**的类名(mark_module_class 同步记录;表无关的自引用判据)---
|
||
# 为什么需要:自引用字段(如 includes/ast/base.vp `AST.children: list[rawptr[AST]]`)
|
||
# 解析时本类尚未 register_struct ⇒ 注册表查不到;而 ModuleClasses 表可能因
|
||
# "登记时表未就绪/键随 pool 回收"而查不到 ⇒ 实参留在裸名,与同一类在形参注解/
|
||
# 构造调用处的带 SHA1 形式分裂成两个特化名(现场:base.vp:359 `_append_child(
|
||
# self.children, ...)` 源 %"list[rawptr[AST]]"* vs 目标 %"list[rawptr<sha1>.AST]"*,
|
||
# 被"非基本类型指针不可隐式转换"拦下)。类名直接来自类定义入口,不经任何表 ⇒
|
||
# 该窗口内**必然**可判(与 CurDefModuleSha1 同源、同生命周期)。
|
||
CurDefClassName: str
|
||
|
||
# --- 本文件顶层 ClassDef 的类名表(每文件重建;register_file_classes 登记)---
|
||
# 为什么需要(**第二个"裸类名"窗口**):Translator.translate 的 prologue 先翻译
|
||
# 模块级变量与前向声明(Translator.vp:592-601),**早于** translate_children(:602)
|
||
# —— 此时"正在定义类"窗口尚未建立(CurDefModuleSha1=None),规则 5.5 的证据
|
||
# ①②③④ 全落空 ⇒ 形参/全局注解里的裸类名(如 includes/ast/base.vp:148
|
||
# `_append_child(..., lst: list[rawptr[AST]])`)以**裸名**形式被首次注册并被后续
|
||
# 复用(首写者胜),与该类自身翻译期解析出的带 SHA1 形式分裂
|
||
# (现场:base.vp:359 报"非基本类型指针不可隐式转换")。
|
||
# 本表与"窗口"无关:判据是**正向事实**"该名字是本文件声明的类"(文件级预扫描
|
||
# 在本文件任何注解解析之前登记),配合 FileModuleSha1 即得归属,不猜。
|
||
FileClassNames: hashtable.HashTable*
|
||
|
||
# 本文件(正在翻译的文件)的模块 SHA1:由 Translator 文件入口随预扫描传入。
|
||
# 仅与 FileClassNames 命中**配合**使用 ⇒ 即便窗口里留着"上一文件的旧值"也不会
|
||
# 误归属(旧 sha1 与"本文件声明了该类"两个正向事实不会同时成立)。
|
||
FileModuleSha1: str
|
||
|
||
# 本文件顶层 FunctionDef 的函数名表:用途见 register_file_funcs ——
|
||
# 依赖 stub 注入的**裸名**声明(extern/state/export,不带 SHA1 前缀)若与
|
||
# 本文件自己的声明同名,会先占用该名字,使本文件声明在
|
||
# forward_declare_functions 里被静默丢弃(调用点改按依赖签名 coerce)。
|
||
# 注入侧(VpiReader._inject_function_declare)据此跳过这些名字。
|
||
FileFuncNames: hashtable.HashTable*
|
||
|
||
# --- 名 | 类型 下标索引(查找快速路径;索引不可信时回退原线性扫描) ---
|
||
StructNameIdx: sizet* # 名 → 首注册下标(槽 u64:0=空槽,否则 index+1)
|
||
StructTypeIdx: sizet* # 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.ModuleClasses = None
|
||
self.CurDefModuleSha1 = None
|
||
self.CurDefClassName = None
|
||
self.FileClassNames = None
|
||
self.FileModuleSha1 = None
|
||
self.FileFuncNames = None
|
||
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: memhub.MemBuddy*, strict: int) -> None:
|
||
"""重置可见性状态(每个文件翻译前调用)
|
||
|
||
pool: 内存分配器(严格模式下创建 HashTable)
|
||
strict: 1=严格模式(用户文件),0=宽松模式(includes 文件)
|
||
|
||
`ModuleClasses` 也在此**每文件重建**:它的键是 pool 字符串,而 pool 在
|
||
文件/遍切换时会被 reset ⇒ 若表跨文件长期存活,`_find_slot` 会 strcmp 到
|
||
已回收的键(现场:ASan access-violation,rcx=0x1,栈
|
||
mark_module_class → HashTable.set_int → _resize → _find_slot → strcmp)。
|
||
与 `VisibleStructs` 同口径(同样每文件新建、键同池),故无悬空。
|
||
`CurDefModuleSha1` 指向 `trans.ModuleSha1`(同一 pool 的串),同样必须
|
||
每文件清空,否则下一次 strcat/strcmp 读的是已回收内存。
|
||
"""
|
||
self.StrictVisibility = strict
|
||
self.CurDefModuleSha1 = None
|
||
self.CurDefClassName = None
|
||
# 文件级类名表同口径每文件重建(键与表同池 ⇒ 无悬空;由 Translator 文件入口的
|
||
# 预扫描重新登记,见 register_file_classes)。FileModuleSha1 指向 trans.ModuleSha1
|
||
# (同池串)⇒ 必须一并清空,避免下一文件读到已回收内存。
|
||
self.FileClassNames = None
|
||
self.FileModuleSha1 = None
|
||
self.FileFuncNames = None
|
||
self.ModuleClasses = hashtable.HashTable(pool)
|
||
if strict != 0:
|
||
# 严格模式:创建新的 HashTable 记录可见结构体
|
||
self.VisibleStructs = hashtable.HashTable(pool)
|
||
else:
|
||
# 宽松模式:全部可见,不需要 HashTable
|
||
self.VisibleStructs = None
|
||
|
||
def add_visible_struct(self, pool: 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 _module_class_key(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> str:
|
||
"""拼 "{sha1}.{类名}" 查询/登记键(分配在给定 pool,调用方无需释放)。"""
|
||
if pool is None or sha1 is None or name is None:
|
||
return None
|
||
k_len: sizet = string.strlen(sha1) + 1 + string.strlen(name) + 1
|
||
k_buf: str = pool.alloc(k_len)
|
||
if k_buf is None:
|
||
return None
|
||
k_buf[0] = '\0'
|
||
string.strcat(k_buf, sha1)
|
||
string.strcat(k_buf, ".")
|
||
string.strcat(k_buf, name)
|
||
return k_buf
|
||
|
||
def mark_module_class(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> None:
|
||
"""登记"{sha1}.{类名}":**该模块声明了这个类**(类定义入口调用一次)。
|
||
|
||
键里带 SHA1 ⇒ 天然按"声明模块"区分。表由 `reset_visible_structs` **每文件
|
||
重建**(与键同池 ⇒ 无悬空),因此这里的登记只在本文件本遍内有效 ——
|
||
恰好覆盖使用者(`_norm_type_arg_name` 规则 5.5)的窗口:判"类定义中"的
|
||
自引用字段时,登记与查询在同一文件同一遍内。
|
||
|
||
`CurDefModuleSha1` 同步记录("正在定义类所属模块"的权威来源,任何翻译
|
||
路径上都是 trans.ModuleSha1)。
|
||
"""
|
||
if sha1 is None or name is None:
|
||
return
|
||
self.CurDefModuleSha1 = sha1
|
||
# 类名同步记录(表无关的自引用判据;池/表缺失也必须记 —— 它只是当前
|
||
# 类定义入口手里的那个串,不经任何表查询,见 _norm_type_arg_name 规则 5.5)。
|
||
self.CurDefClassName = name
|
||
if pool is None or self.ModuleClasses is None:
|
||
# 未按文件初始化(该路径未调用 reset_visible_structs):不登记。
|
||
# 不在此懒建表 —— 表必须与键同池、且随文件重置,否则会 strcmp 到
|
||
# 已回收的键(ASan access-violation 现场)。
|
||
return
|
||
k_buf: str = self._module_class_key(pool, sha1, name)
|
||
if k_buf is None:
|
||
return
|
||
if self.ModuleClasses.__contains__(k_buf) != 0:
|
||
return
|
||
self.ModuleClasses.set_int(k_buf, 1)
|
||
|
||
def is_module_class(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> int:
|
||
"""裸名 name 是否由模块 sha1 声明(1=是 / 0=否;与 struct 注册状态无关)。"""
|
||
if pool is None or sha1 is None or name is None:
|
||
return 0
|
||
if self.ModuleClasses is None:
|
||
return 0
|
||
k_buf: str = self._module_class_key(pool, sha1, name)
|
||
if k_buf is None:
|
||
return 0
|
||
return self.ModuleClasses.__contains__(k_buf)
|
||
|
||
def register_file_classes(self, pool: memhub.MemBuddy*, sha1: str, tree: ast.AST*) -> None:
|
||
"""文件级预登记:把**本文件顶层 ClassDef 的类名**与**本文件模块 SHA1** 记下来。
|
||
|
||
用途(`_norm_type_arg_name` 规则 5.5 的第五类证据):Translator.translate 的
|
||
prologue 会先翻译**模块级变量**与**前向声明**(Translator.vp:592-601),此后才
|
||
translate_children(:602) 逐个翻译类 —— 前者解析注解时"正在定义类"窗口尚未建立
|
||
(CurDefModuleSha1==None),规则 5.5 的四条证据全落空 ⇒ 注解里的裸类名
|
||
(如 includes/ast/base.vp:148 `_append_child(..., lst: list[rawptr[AST]])` 的
|
||
形参、或模块级全局的注解)以**裸名**形式被首次注册、并被后续复用(首写者胜),
|
||
最终与该类自身翻译期解析出的带 SHA1 形式分裂成两个特化名,报"被指类型是非基本
|
||
类型……不可隐式转换"(现场:base.vp:359)。
|
||
|
||
判据是**正向事实**(该名字确由本文件声明),且与"窗口"无关 ⇒ 不依赖类定义顺序,
|
||
不需要在窗口里"猜"归属。表与键同池、每文件重建(见 reset_visible_structs)。
|
||
"""
|
||
if pool is None or sha1 is None or tree is None:
|
||
return
|
||
self.FileModuleSha1 = sha1
|
||
ch: list[ast.AST*]* = tree.children
|
||
if ch is None:
|
||
return
|
||
if self.FileClassNames is None:
|
||
self.FileClassNames = hashtable.HashTable(pool)
|
||
if self.FileClassNames is None:
|
||
return
|
||
cn: sizet = len(ch)
|
||
ci: sizet
|
||
for ci in range(cn):
|
||
child: ast.AST* = ch.get(ci)
|
||
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
|
||
if self.FileClassNames.__contains__(cd.name) != 0:
|
||
continue
|
||
# 键必须与表同池(AST 的 name 串零拷贝指向 src_buf,跨阶段可能失效):
|
||
# 复制到 pool 后再登记,避免表 resize/查询时 strcmp 到悬空内存。
|
||
nm_len: sizet = string.strlen(cd.name)
|
||
nm_buf: str = pool.alloc(nm_len + 1)
|
||
if nm_buf is None:
|
||
continue
|
||
string.strcpy(nm_buf, cd.name)
|
||
self.FileClassNames.set_int(nm_buf, 1)
|
||
|
||
def is_file_class(self, pool: memhub.MemBuddy*, name: str) -> int:
|
||
"""裸名 name 是否为本文件顶层声明的类(1=是 / 0=否;与窗口、与注册状态无关)。"""
|
||
if name is None or self.FileClassNames is None:
|
||
return 0
|
||
return self.FileClassNames.__contains__(name)
|
||
|
||
def register_file_funcs(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> None:
|
||
"""文件级预登记:把**本文件顶层 FunctionDef 的函数名**记下来。
|
||
|
||
用途(依赖 stub 注入的裸名去重):编译本文件时,prologue 会**先**把依赖模块的
|
||
.vpi 注入进来(StubMergerBuild.LoadDepVpiIntoModule);而 extern/state/export
|
||
声明的符号**不带 SHA1 前缀**(FuncGenDef.vp:172-173 `mangled_name = fd.name`)
|
||
⇒ 依赖的裸名声明先占用该名字;随后 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"*」。
|
||
|
||
注入侧据本表跳过"本文件已声明的裸名"(VpiReader._inject_function_declare),
|
||
使本文件自己的声明优先。表与键同池、每文件重建(与 FileClassNames 同规矩)。
|
||
"""
|
||
if pool is None or tree is None:
|
||
return
|
||
ch: list[ast.AST*]* = tree.children
|
||
if ch is None:
|
||
return
|
||
if self.FileFuncNames is None:
|
||
self.FileFuncNames = hashtable.HashTable(pool)
|
||
if self.FileFuncNames is None:
|
||
return
|
||
cn: sizet = len(ch)
|
||
ci: sizet
|
||
for ci in range(cn):
|
||
child: ast.AST* = ch.get(ci)
|
||
if child is None:
|
||
continue
|
||
if child.kind() != ast.ASTKind.FunctionDef:
|
||
continue
|
||
fd: ast.FunctionDef* = ast.FunctionDef*(child)
|
||
if fd is None or fd.name is None:
|
||
continue
|
||
if self.FileFuncNames.__contains__(fd.name) != 0:
|
||
continue
|
||
# 键必须与表同池(AST 的 name 串零拷贝指向 src_buf,跨阶段可能失效):
|
||
# 复制到 pool 后再登记,避免表 resize/查询时 strcmp 到悬空内存。
|
||
nm_len: sizet = string.strlen(fd.name)
|
||
nm_buf: str = pool.alloc(nm_len + 1)
|
||
if nm_buf is None:
|
||
continue
|
||
string.strcpy(nm_buf, fd.name)
|
||
self.FileFuncNames.set_int(nm_buf, 1)
|
||
|
||
def is_file_func(self, name: str) -> int:
|
||
"""裸名 name 是否为本文件顶层声明的函数(1=是 / 0=否)"""
|
||
if name is None or self.FileFuncNames is None:
|
||
return 0
|
||
return self.FileFuncNames.__contains__(name)
|
||
|
||
def get_file_module_sha1(self) -> str:
|
||
"""本文件(正在翻译的文件)的模块 SHA1(register_file_classes 传入;未登记返回 None)。"""
|
||
return self.FileModuleSha1
|
||
|
||
def init_struct_table(self, pool: 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: 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* = table_pool.alloc(idx_bytes)
|
||
type_idx: sizet* = 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*:
|
||
"""获取第 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*(addr)
|
||
|
||
# 名 | 类型 索引(开放寻址 | 线性探测;槽存 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)后索引槽成失效键,探测时键不等即判定未命中。
|
||
def _struct_entry_name(self, i: int) -> str:
|
||
"""取第 i 个条目的 Name(供索引探测比较),越界/空返回 None"""
|
||
e: GenTypes.StructEntry* = 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* = 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* = 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* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||
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* = 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* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||
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*) -> 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* = 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
|
||
for i in range(self.StructIdxCap):
|
||
slot: sizet* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||
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
|
||
return -1
|
||
|
||
def _struct_type_idx_find(self, tyval: uint64) -> int:
|
||
"""类型索引查首注册下标:>=0 命中;-1 未命中/不可信(须回退扫描)
|
||
|
||
类型索引不保证覆盖全部 Ty 值(如 ClassGenDef 深拷贝后 existing.Ty 被
|
||
改写为新指针,未入索引)⇒ 未命中不能断言"确无",一律回退线性扫描。
|
||
"""
|
||
base: sizet* = 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
|
||
for i in range(self.StructIdxCap):
|
||
slot: sizet* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||
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
|
||
return -1
|
||
|
||
def _get_field_entry(self, struct_entry: GenTypes.StructEntry*, i: int) -> GenTypes.FieldEntry*:
|
||
"""获取结构体中第 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*(addr)
|
||
|
||
def register_struct(self, pool: memhub.MemBuddy*,
|
||
name: str,
|
||
struct_ty: llvmlite.LLVMType*,
|
||
sha1: str = None) -> GenTypes.StructEntry*:
|
||
"""注册结构体类型,返回 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* = 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* = 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: 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 用主 pool,
|
||
# mark1 前不回收同样存活)。不复制则 find_struct_by_module 的 strcmp 读悬空 Name。
|
||
name_pool: 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
|
||
|
||
# ============================================================
|
||
# ensure_forward_class - 按"类名 + 定义模块 SHA1"前向声明一个类
|
||
#
|
||
# 语义对齐 TPC(未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充):
|
||
# 注解里出现"指向尚未注册的类的指针"时,返回 Ptr(本占位.Ty) 而不是匿名的
|
||
# Ptr(i8)——后者会丢类身份、冻结成 i8* 且无法修复(Gen↔Generator 循环依赖
|
||
# 使"按顺序先注册被指类"不可能)。定义侧命中该占位条目时**原位填充**同一
|
||
# Ty 对象(保持 Ty 指针不变),所有已发出的 Ptr 引用继续有效。
|
||
#
|
||
# 幂等:已存在 (class_name, sha1) 条目则直接返回,不重复登记。
|
||
# 返回占位/既有条目;参数非法或表满时返回 None。
|
||
# ============================================================
|
||
def ensure_forward_class(self, pool: memhub.MemBuddy*, sha1: str, class_name: str) -> GenTypes.StructEntry*:
|
||
if pool is None or sha1 is None or class_name is None:
|
||
return None
|
||
if sha1[0] == '\0' or class_name[0] == '\0':
|
||
return None
|
||
existing: GenTypes.StructEntry* = self.find_struct_by_module(class_name, sha1)
|
||
if existing is not None:
|
||
return existing
|
||
if self.init_struct_table(pool) == 0:
|
||
return None
|
||
if self.StructCount >= STRUCT_MAX:
|
||
VLogger.error(f"table full, cannot forward-declare {class_name}", "STRUCT")
|
||
return None
|
||
# 具名不透明结构体 "%{sha}.{Class}"(fields=None, fcount=0):名称与定义侧
|
||
# ClassGenDef 的 "sha1.ClassName" 逐字一致,故定义侧能按名定位并原位填充。
|
||
# 名称与 Ty 必须按 StdlibAlloc 选**持久池**(与 register_struct 同口径):
|
||
# 占位条目不随本文件 reset 回收,Ty/Name 若在瞬时 pool 会悬空。
|
||
fwd_pool: memhub.MemBuddy* = pool
|
||
if self.StdlibAlloc != 0:
|
||
fwd_pool = CompilerFactory.factory.PermPool
|
||
if fwd_pool is None:
|
||
fwd_pool = pool
|
||
sh_len: sizet = string.strlen(sha1)
|
||
cn_len: sizet = string.strlen(class_name)
|
||
name_buf: str = fwd_pool.alloc(sh_len + 1 + cn_len + 1)
|
||
if name_buf is None:
|
||
return None
|
||
string.strcpy(name_buf, sha1)
|
||
name_buf[sh_len] = '.'
|
||
string.strcpy(name_buf + sh_len + 1, class_name)
|
||
fwd_ty: llvmlite.LLVMType* = llvmlite.Struct(fwd_pool, None, 0, name_buf)
|
||
if fwd_ty is None:
|
||
return None
|
||
entry: GenTypes.StructEntry* = self.register_struct(pool, class_name, fwd_ty, sha1)
|
||
if entry is None:
|
||
return None
|
||
entry.IsForwardDecl = 1
|
||
self.add_visible_struct(pool, class_name)
|
||
return entry
|
||
|
||
def register_struct_alias(self, pool: memhub.MemBuddy*,
|
||
name: str,
|
||
sha1: str,
|
||
src_entry: GenTypes.StructEntry*) -> GenTypes.StructEntry*:
|
||
"""为已注册类追加包重导出别名 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* = self.find_struct_by_module(name, sha1)
|
||
if existing is not None: return existing
|
||
if self.StructCount >= STRUCT_MAX: return None
|
||
entry: GenTypes.StructEntry* = self._get_struct_entry(self.StructCount)
|
||
if entry is None: return None
|
||
# 复制类名与 SHA1(别名注册同样按 StdlibAlloc 选分配源,跨 1a/1b reset 存活)
|
||
alias_pool: 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: 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* = 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: 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* = self.find_struct(class_name)
|
||
if entry is None:
|
||
return None
|
||
return entry.ModuleSha1
|
||
|
||
def add_field(self, pool: memhub.MemBuddy*,
|
||
struct_entry: GenTypes.StructEntry*,
|
||
field_name: str,
|
||
field_ty: llvmlite.LLVMType*,
|
||
default_val: ast.AST* = 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* = self._get_field_entry(struct_entry, idx)
|
||
if fe is None: return -1
|
||
# 复制字段名(StdlibAlloc=1 用永久池跨 reset 存活);否则 lookup_field 的
|
||
# strcmp(fe.Name, ...) 读悬空越界。
|
||
fname_pool: 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)
|
||
# 带调用点 tag:写入者若为该 32B 附近的字段名缓冲,报文直接指名本处。
|
||
name_buf: str = fname_pool.alloc_tagged(name_len + 1, memhub.DETSITE_STRUCTGENMIXIN_570)
|
||
if name_buf is not None:
|
||
string.strcpy(name_buf, field_name)
|
||
fe.Name = name_buf
|
||
fe.Index = idx
|
||
# 1b 阶段(StdlibAlloc=1)字段类型必须深拷贝到 stdlib:字段级 Ty 若仍存 pool
|
||
# 指针,本文件 reset_to(mark1) 回收后跨文件读取(字段展平、多继承 this 调整、
|
||
# _apply_struct_defaults 的 build_gep_struct 用 fe.Ty 作 GEP 结果类型)会解引用
|
||
# 悬空内存。
|
||
# 注:注解类名已并入 Spec.SourceName,Spec 一律 PermPool 分配 ⇒ 天然跨 reset 存活。
|
||
if self.StdlibAlloc != 0:
|
||
dp_pool_sg: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||
if dp_pool_sg is None: dp_pool_sg = pool
|
||
ty_copy: llvmlite.LLVMType* = 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
|
||
# 实例一律 PermPool 分配(驻留或独立副本)⇒ 天然跨 1b reset 存活。
|
||
arena_f: TSpec.TypeSpecArena* = 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)
|
||
|
||
# const 标记显式置 0(不依赖条目内存的零初值约定):本字段由 ClassGenDef
|
||
# 在确认为 const 对象时才置 1,显式清零杜绝"未初始化即被读成 1"的误报。
|
||
fe.IsConst = 0
|
||
struct_entry.FieldCount = idx + 1
|
||
return idx
|
||
|
||
def _deep_copy_default_ast(self, pool: memhub.MemBuddy*, default_val: ast.AST*) -> ast.AST*:
|
||
"""深拷贝字段默认值 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* = ast.Constant*(default_val)
|
||
dst_c: ast.Constant* = ast.Constant*(pool.alloc_tagged(ast.Constant.__sizeof__(), memhub.DETSITE_STRUCTGENMIXIN_641))
|
||
if dst_c is None:
|
||
return None
|
||
string.memset(dst_c, 0, ast.Constant.__sizeof__())
|
||
# 拷贝 vtable 指针(AST 是 @t.CVTable,kind() 虚调用依赖首字段 vptr)
|
||
# 【必须与 _deep_copy_list_ast 同款:c.Deref 读首字 + c.DerefAs 写首字】
|
||
# 旧写法(本行 + 下一行)在 TPC 下**静默失效**,是 vptr=0 AST 节点的产生源:
|
||
# ① `vt_ptr: rawptr = rawptr(*(uint64*)(default_val))` 被折成裸 0
|
||
# ——IR 实证 `inttoptr i32 0 to i8*`(**没有 load vptr 的指令**);
|
||
# ② `*dst_c = vt_ptr`(目标 LHS 是结构体类型的解引用)**根本不发射任何
|
||
# 指令**——全模块无任何指令带 `!dbg !425`。
|
||
# ⇒ dst_c 的前 8 字节保持 memset 的 0(vptr = 0),随后对它的
|
||
# `node.kind()` 虚调用生成 `load i8*, i8** [0 + 0]` ⇒ ASan READ at 0x0。
|
||
# 逐字 IR 证据:`TransPyV\output\b5883dc9c114a604.ll:10382-10384`
|
||
# (!424 = 本函数 :587 行;!426 = :590 行;两者之间无任何指令)。
|
||
# 对照可用写法(List 分支,同文件 :10611-10615):`bitcast AST* → i64*` +
|
||
# `load i64` + `inttoptr i64 → i8*` + `bitcast List* → i8**` + `store i8*`。
|
||
vt_ptr_c: t.CPtr = t.CPtr(c.Deref((uint64*)(default_val)))
|
||
c.DerefAs(dst_c, vt_ptr_c)
|
||
# 拷贝位置字段(报错定位用)
|
||
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_tagged(s_len + 1, memhub.DETSITE_STRUCTGENMIXIN_659)
|
||
if s_copy is not None:
|
||
string.strcpy(s_copy, src_c.str_val)
|
||
dst_c.str_val = s_copy
|
||
return (ast.AST*)(dst_c)
|
||
|
||
def _deep_copy_list_ast(self, pool: memhub.MemBuddy*, default_val: ast.AST*) -> ast.AST*:
|
||
"""深拷贝 List 节点(列表字面量默认值)到永久池,跨 1b reset 存活。
|
||
|
||
elts 容器手动在永久池构造(list 的 __pool__ 置 None:深拷贝的 elts
|
||
只被只读访问 get/__len__/迭代,不 append,不会触发 pool 扩容)。
|
||
元素递归调用 _deep_copy_default_ast(支持 Constant 与嵌套 List)。
|
||
"""
|
||
src_l: ast.List* = ast.List*(default_val)
|
||
dst_l: ast.List* = ast.List*(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.CPtr(c.Deref((uint64*)(default_val)))
|
||
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*]* = src_l.elts
|
||
if src_elts_l is not None:
|
||
cnt_l: sizet = len(src_elts_l)
|
||
lst_copy_l: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(pool.alloc_tagged(list[ast.AST*].__sizeof__(), memhub.DETSITE_STRUCTGENMIXIN_693))
|
||
if lst_copy_l is not None:
|
||
string.memset(lst_copy_l, 0, list[ast.AST*].__sizeof__())
|
||
# 计数**绝不先按源计数写死**:必须在填充循环里按"实际写入槽数"落定
|
||
# (下方 written_l)。否则源元素为空指针 / 不可深拷贝而被静默跳过时,
|
||
# 会留下 __count__ > 实际填充数 的零槽 ⇒ 后续 _init_global_array_from_list
|
||
# 的 elts.get(i) 读到裸 0 ⇒ 空 AST 元素崩溃(ASan access-violation 0x0)。
|
||
# 本处只先定容量。
|
||
lst_copy_l.__capacity__ = cnt_l
|
||
# 元素尺寸的唯一真相源:申请(cnt_l * elem_sz_l)、清零(同式)与
|
||
# 写入(槽基址按 elem_sz_l 步进)必须同源;写成两处字面量 8 会让
|
||
# "申请 n*8 却按别的宽度写"重新变得可能。
|
||
elem_sz_l: sizet = 8
|
||
lst_copy_l.__elem_size__ = elem_sz_l
|
||
lst_copy_l.__pool__ = None
|
||
data_l: t.CPtr = pool.alloc_tagged(cnt_l * elem_sz_l,
|
||
memhub.DETSITE_STRUCTGENMIXIN_698)
|
||
if data_l is not None:
|
||
string.memset(data_l, 0, cnt_l * elem_sz_l)
|
||
lst_copy_l.__data__ = data_l
|
||
written_l: sizet = 0
|
||
li_l: sizet
|
||
for li_l in range(cnt_l):
|
||
elem_src_l: ast.AST* = src_elts_l.get(li_l)
|
||
# 显式判空(本仓硬约定:指针判空一律 `is None`)。上一轮曾写作
|
||
# 整型字面量比较 `elem_src_l == 0` 并附注"与 `is None` 同义":
|
||
# 该注的 IR 事实对(`x == 0` 同样编译为 `icmp eq <ptr>, null`,
|
||
# 逐字证据见 AssignGenMixin.vp:1126-1145 记录的反例现场),
|
||
# 但结论表述误导——`== 0` 并非无效判据,只是不该作为本仓判空写法。
|
||
# 源列表自身存在空元素,说明产生源已损坏(计数 > 实际填充)
|
||
# ——响亮失败,绝不静默补 0。
|
||
if elem_src_l is None:
|
||
self.Gen.TypeGenErr.fatal_error(default_val,
|
||
f"字段默认值深拷贝:列表字面量第 {li_l} 个元素为空 AST 元素(指针为 0)。"
|
||
f"上层某处产生了空 AST 元素(元素计数与实际填充不一致),"
|
||
f"拒绝静默补 0 生成残缺的默认值;已终止编译。",
|
||
"ValueError")
|
||
return None
|
||
elem_dst_l: ast.AST* = self._deep_copy_default_ast(pool, elem_src_l)
|
||
if elem_dst_l is None:
|
||
self.Gen.TypeGenErr.fatal_error(elem_src_l,
|
||
f"字段默认值深拷贝:列表字面量第 {li_l} 个元素(kind={elem_src_l.kind()})"
|
||
f"既非 Constant 也非 List,无法深拷贝到永久池。若在此静默跳过该槽,"
|
||
f"会形成【计数 > 实际填充数】的空槽(后续 elts.get 读到裸 0 崩溃);"
|
||
f"若静默截短计数则会改变数组初始化长度。两者都是静默降级,故响亮失败。",
|
||
"TypeError")
|
||
return None
|
||
# 槽元素类型必须是**指针**:`ast.AST*` 的 `[0]` 是
|
||
# 对象 `ast.AST`(48 字节)⇒ `slot[0] = <指针>` 被静默按
|
||
# "结构体赋值"编译成 48 字节 load + 48 字节 store,而本
|
||
# 缓冲只申请 cnt_l*elem_sz_l ⇒ 每槽越界 40 字节,写穿本块
|
||
# canary 与右邻空闲块首字(三代 [FREEFRAME] 现场根因)。
|
||
# 按本仓既有合并规则(hashtable.vp:82-85):`X | t.CPtr`
|
||
# = Ptr(resolve(X)) ⇒ 取下标得到**指针元素** ⇒ 8 字节
|
||
# 指针 store,与申请宽度同源。
|
||
slot_l: ast.AST** = (ast.AST**)(uint64(data_l) + li_l * elem_sz_l)
|
||
slot_l[0] = elem_dst_l
|
||
written_l += 1
|
||
# 计数 == 实际填充数(成功时 == cnt_l;任何不一致都已在上面终止)。
|
||
lst_copy_l.__count__ = written_l
|
||
dst_l.elts = lst_copy_l
|
||
return (ast.AST*)(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_entry_with_ty(self, name: str, alt_name: str) -> GenTypes.StructEntry*:
|
||
"""按名字(含备选名)查**带有效 Ty** 的结构体条目;找不到返回 None。
|
||
|
||
为什么需要它:`find_struct` 的语义是"首注册命中"。D30 的 REnum 重注册把旧
|
||
entry **就地中和**(`Name[0]='\\0'` / `Ty=None` / `FieldCount=0`)后再追加新
|
||
entry;若名索引/线性扫描先命中那条已中和的旧条目,调用方拿到的 `Ty` 是 None ——
|
||
依赖 `Ty` 的代码(如 match 的 REnum 变体字段绑定)会**静默跳过**,症状表现为
|
||
case 绑定名未定义(NameError,真因被藏到下游)。本函数跳过 `Ty` 无效的同名条目,
|
||
返回首个 `Ty` 有效的同名(或备选名)条目;都没有则返回 None,由调用方决定报错。
|
||
|
||
name / alt_name:主名与备选名(如模块限定名与裸类名),任一命中即可。
|
||
"""
|
||
if self.StructTable is None:
|
||
return None
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||
if entry is None or entry.Name is None or entry.Ty is None:
|
||
continue
|
||
if entry.Name[0] == '\0':
|
||
continue
|
||
if string.strcmp(entry.Name, name) == 0:
|
||
return entry
|
||
if alt_name is not None and string.strcmp(entry.Name, alt_name) == 0:
|
||
return entry
|
||
return None
|
||
|
||
def find_struct(self, name: str) -> GenTypes.StructEntry*:
|
||
"""按类名查找结构体,返回 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* = 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* = \
|
||
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* = \
|
||
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* = None
|
||
for i2 in range(self.StructCount):
|
||
e2: GenTypes.StructEntry* = 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':
|
||
# 同名唯一性检查(对齐上方限定名路径):跨模块同名类(如 VKernel gfx
|
||
# 与 App gfx 都定义 Scene3D)存在时不得返回先注册的错误 entry——否则
|
||
# 模块段无法解析的限定名被劫持到错误模块 → 返回类型解析查错 text.ll。
|
||
same_cnt_b: int = 0
|
||
same_entry_b: GenTypes.StructEntry* = None
|
||
for i in range(self.StructCount):
|
||
entry: GenTypes.StructEntry* = 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*:
|
||
"""按类名 + 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* = 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* = 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。
|
||
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*) -> GenTypes.StructEntry*:
|
||
"""按类型指针查找结构体(指针值比较),返回 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* = 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*) -> 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* = 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
|
||
|
||
# ============================================================
|
||
# verify_field_slot_consistency - 字段表 ↔ 结构体槽位一致性护栏(fail-fast)
|
||
#
|
||
# 背景(真实崩溃,2026-09-27):模块 8a96a2885bae09ca(ExprGenOps)把
|
||
# `ast.Name.id` 编译成 GEP 下标 9(该槽是 ctx,i32),而**同一次构建**里
|
||
# 320b55907b889d59(AssignGenMixin)用下标 8(i8*)——两者共用同一 %struct
|
||
# 布局(stub.ll:560 = {i8*, AST*, MemManager*, i32×4, list*, i8*(id), i32(ctx)})。
|
||
# 后果:`_apply_aug_op` → `_node_type_unsigned` 读 `nm.id` 得到 ctx
|
||
# (ASTCtx.Store = 1),`inttoptr i32 1 → i8*` 后进 strlen ⇒
|
||
# ASan access-violation at 0x1(IR 实证:8a96a2885bae09ca.text.ll:1510-1517)。
|
||
#
|
||
# `FieldEntry.Index` 全仓唯一写者是 StructGenMixin.add_field(值 = 当时 FieldCount),
|
||
# 而结构体 type 的槽位由**同一字段列表**另一路生成;两者一旦错位,按 Index
|
||
# 算出的 GEP 就会读到相邻字段(指针被当整数读,或反之)。此处宁可立即报错并
|
||
# 指名 类/字段表条数/槽位数/条目与槽位类型,也不放行按错误偏移算出的 GEP。
|
||
#
|
||
# 只判"明确不同"的类别(指针性 / 整数位宽 / 浮点位宽);结构体/数组等结构类型
|
||
# 不做值比较(避免 DeepCopyType 副本、递归定义的假阳性)。
|
||
# 空字段表(FieldCount=0,vpi 注入的"仅签名类")不判:既有语义是"按名查不到
|
||
# 返回 None",由调用方处理。
|
||
# ============================================================
|
||
def verify_field_slot_consistency(self, struct_ty: llvmlite.LLVMType*,
|
||
entry: GenTypes.StructEntry*) -> int:
|
||
"""字段表条数须等于结构体槽位数;每条目 Ty 须与其 Index 槽位同类"""
|
||
if struct_ty is None or entry is None:
|
||
return 0
|
||
if entry.FieldCount <= 0:
|
||
return 0
|
||
# 联合体豁免:union 的字段表有 N 条(每个成员一条),但 LLVM 布局只有 **1 个
|
||
# 槽位**(成员全部偏移 0、按最大成员定宽),二者本就不相等;字段访问走的是
|
||
# bitcast 到字段类型后的 GEP(见 ExprGenAttr 的 union 分支),不依赖槽位下标,
|
||
# 故本护栏不适用(误报实证:EdgeCaseTest 的 `class Data(t.CUnion)`,2 成员 → 1 槽)。
|
||
if entry.IsUnion != 0 or self.is_union_by_type(struct_ty) != 0:
|
||
return 0
|
||
fcount: int = llvmlite.get_struct_fcount(struct_ty)
|
||
if fcount <= 0:
|
||
return 0
|
||
if entry.FieldCount != fcount:
|
||
self.Gen.TypeGenErr.fatal_error(None,
|
||
f"结构体 '{entry.Name}' 的字段表条数({entry.FieldCount})与 LLVM 槽位数({fcount})不一致:"
|
||
f"字段条目与结构体布局由两条路径产生,错位会让字段访问按错误下标算 GEP"
|
||
f"(实证:ast.Name.id 落到 ctx 槽 ⇒ 指针被读成整数 1 ⇒ strlen(0x1) 崩溃)。",
|
||
"TypeError")
|
||
return 0
|
||
node: rawptr[llvmlite.ParamNode] = llvmlite.get_struct_fields(struct_ty)
|
||
i: int = 0
|
||
while i < entry.FieldCount and node is not None:
|
||
fe: GenTypes.FieldEntry* = self._get_field_entry(entry, i)
|
||
slot_ty: llvmlite.LLVMType* = llvmlite.paramnode_get_ty(node)
|
||
if fe is not None and fe.Name is not None and fe.Ty is not None and slot_ty is not None:
|
||
ptr_f: int = self.Gen.ExprGenBase.is_ptr_type(fe.Ty)
|
||
ptr_s: int = self.Gen.ExprGenBase.is_ptr_type(slot_ty)
|
||
bits_f: int = self.Gen.ExprGenBase.get_llvm_type_bits(fe.Ty)
|
||
bits_s: int = self.Gen.ExprGenBase.get_llvm_type_bits(slot_ty)
|
||
fbits_f: int = self.Gen.ExprGenBase.get_llvm_float_bits(fe.Ty)
|
||
fbits_s: int = self.Gen.ExprGenBase.get_llvm_float_bits(slot_ty)
|
||
if ptr_f != ptr_s or bits_f != bits_s or fbits_f != fbits_s:
|
||
self.Gen.TypeGenErr.fatal_error(None,
|
||
f"结构体 '{entry.Name}' 的字段 '{fe.Name}'(条目下标 {i})与其槽位类型不同类:"
|
||
f"条目 指针性={ptr_f}/整数位宽={bits_f}/浮点位宽={fbits_f},"
|
||
f"槽位 指针性={ptr_s}/整数位宽={bits_s}/浮点位宽={fbits_s}。"
|
||
f"字段条目与结构体布局错位(实证:id 落到 ctx 槽 ⇒ 指针被读成整数 1 ⇒ strlen(0x1) 崩溃)。",
|
||
"TypeError")
|
||
return 0
|
||
node = llvmlite.paramnode_get_next(node)
|
||
i += 1
|
||
return 0
|
||
|
||
def lookup_field(self, struct_ty: llvmlite.LLVMType*,
|
||
field_name: str) -> GenTypes.FieldEntry*:
|
||
"""按结构体类型和字段名查找字段信息
|
||
|
||
两遍查找策略(跨模块同名类冲突的根本修复):
|
||
第一遍: 类型指针 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* = self.find_struct_by_type(struct_ty)
|
||
if lk_entry is not None:
|
||
# 字段表与结构体槽位的一致性护栏(fail-fast):错位即拒绝,见
|
||
# verify_field_slot_consistency 头注释(真实崩溃:ast.Name.id → 槽 9)。
|
||
self.verify_field_slot_consistency(struct_ty, lk_entry)
|
||
for fi in range(lk_entry.FieldCount):
|
||
fe: GenTypes.FieldEntry* = 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 顶掉 → 字段错位
|
||
# → _thread_entry call 0)。strncmp 不分配临时缓冲。
|
||
# 前缀判定走唯一真源 GenApi.sha1_prefix_len。
|
||
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* = 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* = 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 == '[' and 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* = None,
|
||
from_imports: str = None) -> GenTypes.FieldEntry*:
|
||
"""按类名和字段名查找字段信息
|
||
|
||
优先用 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* = 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* = 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)。
|
||
# 注意:泛型特化名含下标,取点必须走 _last_dot_outside_brackets,否则模块名截错。
|
||
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* = \
|
||
self.find_struct_by_module(bare_q, mod_sha1_q)
|
||
if mod_entry_q is not None:
|
||
# 依赖未就绪容忍(对齐裸名路径):FieldCount==0 的空类
|
||
# (注册不完整/源模块未编译)→ 返回 None 让上层标记
|
||
# _deps_pending 下遍重译,不在此 fatal。
|
||
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* = \
|
||
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 变体字段别名回退(与裸名路径一致):模块限定访问
|
||
# (llvmlite.LLVMType.Pointee)类命中后字段未找到时,直接查
|
||
# 变体别名(REnum 字段是 _p1/_p2 槽位)。此前该回退只在裸名
|
||
# 路径,模块限定路径此处 fatal 先触发 → 永远走不到。
|
||
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* = \
|
||
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* = 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 槽位别名。若留给调用方回退,本函数字段未找到
|
||
# 时的 fatal 会先 sys.exit(1),调用方回退永远执行不到,故此处直接查变体别名。
|
||
# 注意:_lookup_renum_field_alias 用 class_name 指针值比较,必须传 entry.Name
|
||
# (register_struct 复制 + _update_renum_alias_class_ptr 统一后的池指针)。
|
||
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* = \
|
||
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* = 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*:
|
||
"""按类名获取结构体的 LLVM 类型"""
|
||
entry: GenTypes.StructEntry* = 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*:
|
||
"""按类名和字段索引获取字段信息"""
|
||
if class_name is None:
|
||
return None
|
||
entry: GenTypes.StructEntry* = 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* = self.find_struct(class_name)
|
||
if entry is None:
|
||
return 0
|
||
for fi in range(entry.FieldCount):
|
||
fe: GenTypes.FieldEntry* = 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*) -> llvmlite.LLVMType*:
|
||
"""从 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* = val.Ty
|
||
while ty is not None:
|
||
# 同类编译 bug(见 TypeGenResolve L1463 注释):case body 直接
|
||
# 更新 while 条件变量(ty = pointee)时自举编译出的 match 把赋值
|
||
# 写到 subject 快照槽,while 条件读旧槽 → 死循环。改法:case
|
||
# 绑定到局部变量,match 外更新循环变量。
|
||
nxt_ty: llvmlite.LLVMType* = 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* = 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* = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.IsUnion
|
||
|
||
def is_union_by_type(self, struct_ty: llvmlite.LLVMType*) -> 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* = 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* = 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* = 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* = 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* = 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* = 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* = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
return entry.HasNew
|
||
|
||
def get_class_name_by_type(self, pool: memhub.MemBuddy*,
|
||
struct_ty: llvmlite.LLVMType*) -> 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* = 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* = 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*) -> 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* = 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* = 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* = 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* = 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* = self.find_struct(name)
|
||
if entry is None:
|
||
return 0
|
||
entry.ParentName = parent_name
|
||
return 1
|
||
|
||
def set_parent_names(self, pool: memhub.MemBuddy*,
|
||
name: str, names_ptr: sizet*,
|
||
count: int) -> int:
|
||
"""设置结构体的父类名数组(永久池深拷贝,跨文件永久存活)"""
|
||
if name is None:
|
||
return 0
|
||
entry: GenTypes.StructEntry* = 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: memhub.MemBuddy* = pool
|
||
if self.StdlibAlloc != 0:
|
||
pn_pool = CompilerFactory.factory.PermPool
|
||
if pn_pool is None:
|
||
pn_pool = pool
|
||
arr: sizet* = 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 = rawptr(addr)
|
||
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* = 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* = self.find_struct(name)
|
||
if entry is None or entry.ParentNames is None:
|
||
return None
|
||
if idx >= entry.ParentCount:
|
||
return None
|
||
parr: sizet* = (sizet*)(entry.ParentNames)
|
||
addr: sizet = parr[idx]
|
||
if addr == 0:
|
||
return None
|
||
return rawptr(addr)
|
||
|
||
def get_mro_chain(self, pool: memhub.MemBuddy*,
|
||
name: str) -> sizet*:
|
||
"""MRO 线性化类名数组(深度优先 + 声明顺序 + 去重,含自身)"""
|
||
if pool is None or name is None:
|
||
return None
|
||
arr: sizet* = pool.alloc(8 * (MRO_MAX + 1))
|
||
if arr is None:
|
||
return None
|
||
stack: sizet* = pool.alloc(8 * MRO_MAX)
|
||
if stack is None:
|
||
return None
|
||
visited: sizet* = 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 = rawptr(cur_addr)
|
||
if cur is None:
|
||
continue
|
||
# 已访问检查(线性搜索)
|
||
dup: int = 0
|
||
for vi in range(visit_count):
|
||
va: sizet = visited[vi]
|
||
if va != 0:
|
||
vn: str = rawptr(va)
|
||
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* = 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* = (sizet*)(entry.ParentNames)
|
||
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* = 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* = 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* = self.find_struct(name)
|
||
if entry is None or entry.VTableMethods is None:
|
||
return -1
|
||
# VTableMethods 字段声明为 char*,但实际存储的是 sizet 数组
|
||
# 必须转换为 sizet* 才能正确读取 8 字节指针值
|
||
methods_arr: sizet* = (sizet*)(entry.VTableMethods)
|
||
for i in range(entry.VTableMethodCount):
|
||
mname_addr: sizet = methods_arr[i]
|
||
if mname_addr == 0:
|
||
continue
|
||
mname: str = rawptr(mname_addr)
|
||
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* = 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* = self.find_struct(name)
|
||
if entry is None or entry.VTableMethods is None:
|
||
return None
|
||
if idx >= entry.VTableMethodCount:
|
||
return None
|
||
methods_arr: sizet* = (sizet*)(entry.VTableMethods)
|
||
mname_addr: sizet = methods_arr[idx]
|
||
if mname_addr == 0:
|
||
return None
|
||
return rawptr(mname_addr)
|
||
|
||
def get_field_count(self, name: str) -> int:
|
||
"""获取结构体字段数量,返回 -1=未找到"""
|
||
if name is None:
|
||
return -1
|
||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||
if entry is None:
|
||
return -1
|
||
return entry.FieldCount
|
||
|
||
def ensure_struct_def_in_module(self, pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
ty: llvmlite.LLVMType*):
|
||
"""确保跨模块结构体的完整定义在当前模块中可用(供 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_tagged(32,
|
||
memhub.DETSITE_STRUCTGENMIXIN_1804)
|
||
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* = 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: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
ty: llvmlite.LLVMType*):
|
||
"""递归扫描类型树,确保值类型子结构体有完整定义"""
|
||
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: rawptr[llvmlite.ParamNode] = 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):
|
||
# 数组类型:元素如果是值类型结构体,需要完整定义(Array 元素只递归
|
||
# 字段不发射定义 → llc "use of undefined type named '...'")。
|
||
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*) -> str:
|
||
"""获取 FieldEntry 的字段名,返回 None=未设置"""
|
||
if fe is None:
|
||
return None
|
||
return fe.Name
|
||
|
||
def get_field_type_ptr(self, fe: GenTypes.FieldEntry*) -> llvmlite.LLVMType*:
|
||
"""获取 FieldEntry 的字段类型,返回 None=未设置"""
|
||
if fe is None:
|
||
return None
|
||
return fe.Ty
|
||
|
||
def get_field_default_ptr(self, fe: GenTypes.FieldEntry*) -> ast.AST*:
|
||
"""获取 FieldEntry 的默认值 AST 节点,返回 None=无默认值"""
|
||
if fe is None:
|
||
return None
|
||
return fe.DefaultVal
|
||
|
||
def get_field_annot_class_name(self, fe: GenTypes.FieldEntry*) -> str:
|
||
"""获取 FieldEntry 的原始类型注解类名(Spec.SourceName),返回 None=未设置
|
||
|
||
[S4] 原 FieldEntry.AnnotClassName 字段已删除,注解名承载在 Spec.SourceName
|
||
(由 add_field 经 SpecWithSource 写入)。
|
||
"""
|
||
if fe is None:
|
||
return None
|
||
return TSpec.SpecSourceName(fe.Spec)
|
||
|
||
def field_is_volatile(self, fe: GenTypes.FieldEntry*) -> int:
|
||
"""字段是否 volatile(FieldEntry.IsVolatile 承载),1=是 / 0=否
|
||
|
||
由 ClassGenDef 收集字段注解时检测 t.CVolatile 写入;消费点(成员读写的
|
||
load/store)据此加 LLVM volatile 限定符。
|
||
"""
|
||
if fe is None:
|
||
return 0
|
||
return fe.IsVolatile
|
||
|
||
def field_pointee_is_volatile(self, fe: GenTypes.FieldEntry*) -> int:
|
||
"""字段所指物是否 volatile(FieldEntry.PointeeVolatile 承载),1=是 / 0=否
|
||
|
||
字段注解为 rawptr[t.CVolatile | T](等价 C 的 volatile T*)时由 ClassGenDef
|
||
写入;消费点(经该字段解引用 self.ptr[i] / self.ptr.field 的 load/store)
|
||
据此加 LLVM volatile 限定符。
|
||
"""
|
||
if fe is None:
|
||
return 0
|
||
return fe.PointeeVolatile
|
||
|
||
def field_is_const(self, fe: GenTypes.FieldEntry*) -> int:
|
||
"""字段是否 const 对象(FieldEntry.IsConst 承载),1=是 / 0=否
|
||
|
||
字段注解含 t.CConst 且字段类型非指针时由 ClassGenDef 写入;消费点
|
||
(obj.f = ... / self.f = ... 的字段赋值)据此 fail-fast。
|
||
"""
|
||
if fe is None:
|
||
return 0
|
||
return fe.IsConst
|