Files
ViperC/App/lib/core/Gen/StructGenMixin.vp
T

1341 lines
65 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
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
# ============================================================
# 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/...)→ 本类方法
#
# 迁移原则(与 TypeGenMixin P2-1 一致,行为零变化):
# 1. 方法保留原函数签名(含 pool 首参),仅函数体内的全局变量改为 self.字段
# 2. HandlesStruct.vp 原函数保留为"委托":本地模块级单例 _g_gen(BuildPipeline
# /main 注入)cast 成 Generator 类型后调本类方法
# 3. FieldEntry/StructEntry 类型保留在 HandlesStruct.vp(本类字段引用)
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
# ============================================================
# ============================================================
# 常量(与 HandlesStruct 保持一致)
# ============================================================
STRUCT_MAX: t.CDefine = 512
# 字段槽位上限制(与 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
# ============================================================
# 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
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
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 | t.CPtr, 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: memhub.MemBuddy | t.CPtr, 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: memhub.MemBuddy | t.CPtr) -> 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: t.CSizeT = GenTypes.StructEntry.__sizeof__()
table_pool: memhub.MemBuddy | t.CPtr = pool
if self.StdlibAlloc != 0:
table_pool = CompilerFactory.get_perm_pool()
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
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: t.CSizeT = GenTypes.StructEntry.__sizeof__()
addr: t.CUInt64T = t.CUInt64T(self.StructTable) + i * entry_size
return (GenTypes.StructEntry | t.CPtr)(t.CVoid(addr, t.CPtr))
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: t.CSizeT = GenTypes.FieldEntry.__sizeof__()
addr: t.CUInt64T = t.CUInt64T(struct_entry.Fields) + i * field_size
return (GenTypes.FieldEntry | t.CPtr)(t.CVoid(addr, t.CPtr))
def register_struct(self, pool: memhub.MemBuddy | t.CPtr,
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
# 用类型指针去重(同一类型不重复注册,支持跨模块同名类)
# 用 t.CUInt64T 指针值比较(is 比较会触发类型安全检查解引用悬空 Ty)
st_dedup: t.CUInt64T = t.CUInt64T(struct_ty)
for i in range(self.StructCount):
existing: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
if existing is not None and existing.Ty is not None:
if t.CUInt64T(existing.Ty) == st_dedup:
return existing
if self.StructCount >= STRUCT_MAX:
fb_st: str = VLogger.fmt_buf()
if fb_st is not None:
viperlib.snprintf(fb_st, 1024, "table full, cannot register %s", name)
VLogger.error(fb_st, "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: t.CSizeT = GenTypes.FieldEntry.__sizeof__()
if self.StdlibAlloc != 0:
perm_sg: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool()
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: memhub.MemBuddy | t.CPtr = pool
if self.StdlibAlloc != 0:
name_pool = CompilerFactory.get_perm_pool()
if name_pool is None:
name_pool = pool
name_len: t.CSizeT = 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: t.CSizeT = 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
return entry
def set_struct_sha1(self, pool: memhub.MemBuddy | t.CPtr,
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: t.CSizeT = string.strlen(sha1)
sha1_pool: memhub.MemBuddy | t.CPtr = pool
if self.StdlibAlloc != 0:
sha1_pool = CompilerFactory.get_perm_pool()
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: memhub.MemBuddy | t.CPtr,
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:
fb_sf: str = VLogger.fmt_buf()
if fb_sf is not None:
viperlib.snprintf(fb_sf, 1024, "field table full for %s", struct_entry.Name)
VLogger.error(fb_sf, "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: memhub.MemBuddy | t.CPtr = pool
if self.StdlibAlloc != 0:
fname_pool = CompilerFactory.get_perm_pool()
if fname_pool is None:
fname_pool = pool
name_len: t.CSizeT = 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/AnnotClassName 仍存 pool 指针,本文件
# reset_to(mark1) 回收后,跨文件读取(字段展平、多继承 this 调整、
# AnnotClassName 回退、_apply_struct_defaults 的 build_gep_struct 用 fe.Ty
# 作 GEP 结果类型)解引用悬空内存 → 崩溃点漂移(json/string/builtins 之间)。
if self.StdlibAlloc != 0:
dp_pool_sg: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool()
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
if annot_class_name is not None:
annot_len: t.CSizeT = string.strlen(annot_class_name)
annot_buf: str = CompilerFactory.get_perm_pool().alloc(annot_len + 1)
if annot_buf is not None:
string.strcpy(annot_buf, annot_class_name)
fe.AnnotClassName = annot_buf
# 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.AnnotClassName = annot_class_name
fe.DefaultVal = default_val
struct_entry.FieldCount = idx + 1
return idx
def _deep_copy_default_ast(self, pool: memhub.MemBuddy | t.CPtr,
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] = ["py", "pyw"])。
其他非常量默认值(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.CVoid | t.CPtr = t.CVoid(
c.Deref(t.CUInt64T(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: t.CSizeT = 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: memhub.MemBuddy | t.CPtr,
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.CVoid | t.CPtr = t.CVoid(
c.Deref(t.CUInt64T(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: t.CSizeT = 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.CVoid | 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: t.CSizeT
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(t.CUInt64T(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 find_struct(self, name: str) -> GenTypes.StructEntry | t.CPtr:
"""按类名查找结构体,返回 StructEntry 或 None
支持模块限定名(如 "HT.Translator"):
第一遍精确匹配,第二遍按类名(最后一个 '.' 之后的部分)回退
"""
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)
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
# 第二遍:类名回退(处理模块限定名 "HT.Translator" → "Translator")
dot_pos: str = string.strrchr(name, '.')
if dot_pos is not None:
class_name: str = dot_pos + 1
if class_name is not None and class_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, class_name) == 0:
return entry
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:
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:
if entry.ModuleSha1 is not None:
if 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 指针)解引用崩溃。
改用 t.CUInt64T 纯指针值比较,不解引用任何内容。
"""
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: t.CUInt64T = t.CUInt64T(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:
if t.CUInt64T(entry.Ty) == st_val:
return entry
return None
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 身份比较(用 t.CUInt64T 指针值比较,避免 is 编译路径解引用悬空 Ty)
st_lk: t.CUInt64T = t.CUInt64T(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:
if t.CUInt64T(entry.Ty) == st_lk:
# is 匹配,查找字段
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
# is 匹配但字段未找到(类型唯一确定,无需继续)
return None
# 第二遍: 名称回退(is 未匹配到)
ty_name: str = self._extract_struct_name(struct_ty)
if ty_name is not 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:
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 lookup_field_by_class(self, class_name: str,
field_name: str,
sha1: str = None) -> GenTypes.FieldEntry | t.CPtr:
"""按类名和字段名查找字段信息
优先用 SHA1 匹配(跨模块同名类区分),无 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")
bare_name: str = class_name
dot_pos_lfbc: str = string.strrchr(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
# 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个
entry = self.find_struct(class_name)
if entry is None:
# 诊断:目标类未注册
fb_lfbc: str = VLogger.fmt_buf()
if fb_lfbc is not None:
viperlib.snprintf(fb_lfbc, 1024,
"lookup_field_by_class: 类 '%s' 未注册 (struct_count=%d)",
class_name, self.StructCount)
VLogger.error(fb_lfbc, "STRUCT")
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
# 诊断:找到结构体但字段确实未找到,打印字段表
# 注意:VLogger.error 会立即退出进程,字段表须在 error 之前用 warning 打印
for fi in range(entry.FieldCount):
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
if fe is not None:
fb_fn: str = VLogger.fmt_buf()
if fb_fn is not None:
if fe.Name is not None:
viperlib.snprintf(fb_fn, 1024, " 字段[%d]: '%s'", fi, fe.Name)
else:
viperlib.snprintf(fb_fn, 1024, " 字段[%d]: (null)", fi)
VLogger.warning(fb_fn, "STRUCT")
fb_fields: str = VLogger.fmt_buf()
if fb_fields is not None:
if rn_is_renum_l != 0:
# 报错友好化:REnum 类直接说明是"变体字段别名查找失败",并附
# entry.Name 与别名槽位结果,一眼可辨"别名未注册"(槽位=-1)
# 还是"class_name 指针未统一"(已注册但传错指针查不到)
viperlib.snprintf(fb_fields, 1024,
"lookup_field_by_class: 类 '%s' 是 REnum 类(FieldCount=%d),"
"变体字段 '%s' 别名查找失败(entry.Name='%s',别名槽位=%d,"
"请检查该变体字段是否已注册别名,或 class_name 指针是否统一)",
class_name, entry.FieldCount, field_name, rn_cls_l, rn_alias_l)
else:
viperlib.snprintf(fb_fields, 1024,
"lookup_field_by_class: 类 '%s' 已注册 (FieldCount=%d) 但字段 '%s' 未找到",
class_name, entry.FieldCount, field_name)
VLogger.error(fb_fields, "STRUCT")
# 诊断:找到结构体但字段未找到
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:
match ty:
case llvmlite.LLVMType.Ptr(pointee):
ty = pointee
case llvmlite.LLVMType.Struct(fields, fcount, name):
return ty
case _:
return None
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: t.CUInt64T = t.CUInt64T(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 t.CUInt64T(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: memhub.MemBuddy | t.CPtr,
struct_ty: llvmlite.LLVMType | t.CPtr) -> str:
"""按类型指针查找类名,返回类名字符串或 None"""
if struct_ty is None or self.StructTable is None:
return None
st_cn: t.CUInt64T = t.CUInt64T(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:
if t.CUInt64T(entry.Ty) == st_cn:
return entry.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: t.CSizeT = string.strlen(ty_name)
_nlen_cn: t.CSizeT = string.strlen(entry2.Name)
if _tlen_cn > _nlen_cn and ty_name[_tlen_cn - _nlen_cn - 1] == '.':
_tail_ok_cn: int = 1
_ti_cn: t.CSizeT
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: memhub.MemBuddy | t.CPtr,
name: str, names_ptr: t.CSizeT | 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: memhub.MemBuddy | t.CPtr = pool
if self.StdlibAlloc != 0:
pn_pool = CompilerFactory.get_perm_pool()
if pn_pool is None:
pn_pool = pool
arr: t.CSizeT | t.CPtr = pn_pool.alloc(8 * count)
if arr is None:
return 0
for i in range(count):
addr: t.CSizeT = 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: t.CSizeT = string.strlen(src)
dup: str = pn_pool.alloc(slen + 1)
if dup is not None:
string.strcpy(dup, src)
arr[i] = t.CSizeT(dup)
entry.ParentNames = t.CSizeT(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: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(entry.ParentNames, t.CPtr))
addr: t.CSizeT = parr[idx]
if addr == 0:
return None
return (str | t.CPtr)(t.CVoid(addr, t.CPtr))
def get_mro_chain(self, pool: memhub.MemBuddy | t.CPtr,
name: str) -> t.CSizeT | t.CPtr:
"""MRO 线性化类名数组(深度优先 + 声明顺序 + 去重,含自身)"""
if pool is None or name is None:
return None
arr: t.CSizeT | t.CPtr = pool.alloc(8 * (MRO_MAX + 1))
if arr is None:
return None
stack: t.CSizeT | t.CPtr = pool.alloc(8 * MRO_MAX)
if stack is None:
return None
visited: t.CSizeT | 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] = t.CSizeT(name)
stack_count = 1
while stack_count > 0:
stack_count -= 1
cur_addr: t.CSizeT = 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: t.CSizeT = 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] = t.CSizeT(cur)
out_count += 1
if visit_count < MRO_MAX:
visited[visit_count] = t.CSizeT(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: t.CSizeT | t.CPtr = (t.CSizeT | 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 字段声明为 t.CChar*,但实际存储的是 t.CSizeT 数组
# 必须转换为 t.CSizeT* 才能正确读取 8 字节指针值
methods_arr: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(entry.VTableMethods, t.CPtr))
for i in range(entry.VTableMethodCount):
mname_addr: t.CSizeT = 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: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(entry.VTableMethods, t.CPtr))
mname_addr: t.CSizeT = 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: memhub.MemBuddy | t.CPtr,
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: t.CSizeT = string.strlen(sname)
class_name_len: t.CSizeT = string.strlen(class_name)
sha1_len: t.CSizeT = total_len - class_name_len - 1
sha1_buf: str = pool.alloc(32)
if sha1_buf is None:
return
# 32 字节缓冲:strncpy 不截断,前缀超过 31 字符会越界写 + 手动 NUL 再越界
sha1_cp: t.CSizeT = 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)
# 递归扫描字段类型,确保值类型子结构体(如 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 | t.CPtr,
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: t.CInt = 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 的原始类型注解类名,返回 None=未设置"""
if fe is None:
return None
return fe.AnnotClassName