两处均为 a(编译器缺陷),且都被 HandlesImports.py:1055 的
`except Exception -> WARN 忽略异常` 吞掉,表现为模块声明静默退化
(本次现场:VpiReader.vp 阶段 WARN + 结构体声明路径中断)。非源码违规、
非机制漏豁免。
1) lib/core/LLVMCG/StructGen.py
- `_generate_structs` 的「成员表含重名条目、body 已是权威布局」提示调用
`_vlog().warning(...)`,但本文件从未定义/导入 `_vlog` ⇒ NameError。
该分支一旦命中(astaux.ExceptHandler 一类),`_generate_structs` 整体中断。
- 补 `from lib.core.VLogger import get_logger as _vlog`,与同目录
FuncGen.py / TypeConvert.py 同口径。
2) lib/core/Handles/HandlesImports.py:1317
- `_EmitExternalGlobalDeclLlvm` 形参名为 `Node`,该 elif 分支误写成小写 `node`
⇒ 只要传入的是**非带注解**的跨模块全局量(`ast.Assign`)就恒抛 NameError,
分支整体不可达:外部全局变量从未声明 external,消费侧表现为
unknown global / 类型占位。
- 判据改回 `isinstance(Node, ast.Assign)`。
静态核验:对 TransPyC 全部 .py 做 symtable 未定义名扫描,结果 0 条;
`python -m py_compile` 通过。
4032 lines
251 KiB
Python
4032 lines
251 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
|
||
import vast as ast
|
||
import inspect
|
||
import os
|
||
import re
|
||
import hashlib
|
||
import llvmlite.ir as ir
|
||
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
|
||
from lib.core.VLogger import get_logger as _vlog
|
||
from lib.constants.config import mode as _config_mode
|
||
from lib.includes import t
|
||
from lib.includes.t import CTypeRegistry
|
||
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext
|
||
from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords
|
||
from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod
|
||
|
||
|
||
# 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写,
|
||
# 如 `t.CExtern | rawptr[CompilerFactory]`),提取"指针指向的类"时必须滤掉。
|
||
_MARKER_ANNOT_NAMES: frozenset[str] = frozenset({
|
||
'CExtern', 'CExport', 'CStatic', 'CInline', 'CConst', 'CVolatile', 'CRegister',
|
||
'CAuto', 'CDefine', 'CTypedef', 'CPostdefinition', 'CPtr', 'CArrayPtr', 'CVoid',
|
||
'State', 'TLS',
|
||
})
|
||
|
||
# 标记基类名(不是真实父类):与 HandlesClassDef._EmitClassLlvm 的 base 跳过表同口径。
|
||
_MARKER_BASE_NAMES: frozenset[str] = frozenset({
|
||
'Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum',
|
||
'Exception', 'Enum',
|
||
})
|
||
|
||
# LLVM 基础类型表(模块级常量):`_parse_llvm_type_impl` 原先把这份字典**每次调用现建**
|
||
# (profile 2026-09-29:本函数 1,689,809 次调用 ⇒ 字典字面量 1.69M 次、
|
||
# `IntType.__new__` 9.09M 次)。这些基础类型在 llvmlite 里无状态且 IntType 按宽度
|
||
# 缓存同一实例,跨调用/跨 Gen 共享安全 ⇒ 提升为模块常量,表意逐字不变。
|
||
_LLVM_PRIMITIVE_TYPE_MAP: dict[str, ir.Type] = {
|
||
'void': ir.VoidType(),
|
||
'i1': ir.IntType(1),
|
||
'i8': ir.IntType(8),
|
||
'i16': ir.IntType(16),
|
||
'i32': ir.IntType(32),
|
||
'i64': ir.IntType(64),
|
||
'float': ir.FloatType(),
|
||
'double': ir.DoubleType(),
|
||
}
|
||
|
||
|
||
def _HasRealParentClass(Node: ast.ClassDef) -> bool:
|
||
"""类是否声明了**真实父类**(排除 Object/CVTable/NoVTable/CEnum/... 标记基类)。"""
|
||
for base in getattr(Node, 'bases', None) or []:
|
||
base_name: str | None = None
|
||
if hasattr(base, 'id'):
|
||
base_name = base.id
|
||
elif hasattr(base, 'attr'):
|
||
base_name = base.attr
|
||
elif isinstance(base, ast.Subscript):
|
||
if hasattr(base.value, 'id'):
|
||
base_name = base.value.id
|
||
elif hasattr(base.value, 'attr'):
|
||
base_name = base.value.attr
|
||
if base_name and base_name not in _MARKER_BASE_NAMES:
|
||
return True
|
||
return False
|
||
|
||
|
||
def _VtableByClassLevelForImport(Node: ast.ClassDef, Gen: object) -> bool:
|
||
"""跨模块导入类的 VtableByClassLevel(忽略方法级 CVTable 标记的 vtable 状态)。
|
||
|
||
与 HandlesClassDef._resolve_class_flags 的口径一致:类级 @t.CVTable 或
|
||
"有真实父类且父类不是 NoVTable" ⇒ True。供 SymbolUtils.IsVtableSlotMethod
|
||
判定方法是否占虚表槽(唯一的判据源),使导入侧的虚表槽序与定义模块逐槽相同。
|
||
"""
|
||
if getattr(Node, 'decorator_list', None):
|
||
for decorator in Node.decorator_list:
|
||
if isinstance(decorator, ast.Attribute) and decorator.attr == 'CVTable':
|
||
return True
|
||
if isinstance(decorator, ast.Name) and decorator.id == 'CVTable':
|
||
return True
|
||
if not _HasRealParentClass(Node):
|
||
return False
|
||
_novtable_set: set = getattr(Gen, '_cross_module_novtable', None) or set()
|
||
for base in getattr(Node, 'bases', None) or []:
|
||
base_name: str | None = None
|
||
if hasattr(base, 'id'):
|
||
base_name = base.id
|
||
elif hasattr(base, 'attr'):
|
||
base_name = base.attr
|
||
if base_name and base_name not in _MARKER_BASE_NAMES:
|
||
if base_name in _novtable_set:
|
||
return False
|
||
return True
|
||
|
||
|
||
def _ParseFailureSite(Exc: BaseException) -> str:
|
||
"""解析异常 → `行[:列]` 文本;定位信息完全取不到时返回 `'?'`。
|
||
|
||
CPython/vast 的 SyntaxError 若按 (msg, (file, lineno, offset, text)) 构造则带
|
||
`.lineno`/`.offset`;而 TPC 自研 vast 的 Parser/ExprParser 只把行号写进报文
|
||
(`f"... at line {tok.Line}"`,见 lib/vast/Parser.py:826、ExprParser.py:640),
|
||
字段为空 ⇒ 必须正则兜底,否则诊断退化成"无行号",无法直接跳到失败行。
|
||
"""
|
||
Line: int = int(getattr(Exc, 'lineno', 0) or 0)
|
||
Col: int = int(getattr(Exc, 'offset', 0) or 0)
|
||
if not Line:
|
||
Matched: re.Match | None = re.search(r'at line (\d+)', str(Exc))
|
||
if Matched:
|
||
Line = int(Matched.group(1))
|
||
if not Line:
|
||
return '?'
|
||
return f"{Line}:{Col}" if Col else str(Line)
|
||
|
||
|
||
def _ReportDeclarationParseFailure(FilePath: str, Exc: BaseException, Consequence: str) -> None:
|
||
"""声明文件"读 → parse"失败的统一诊断口径(strict fail-fast / 非严格告警)。
|
||
|
||
诊断必带四要素:**文件路径**、**失败行[:列]**、**异常类型与文本**、**后果说明**
|
||
(后果由调用方按点位给出,如"该模块声明将全部缺失,后续可能表现为
|
||
`Undefined function`")。原实现(`_LoadModuleDeclarationsFromFile:364-367`)只在
|
||
strict 模式调 `Trans.LogWarning`——而 `Trans.Warnings` 全仓只被 append、无人消费
|
||
(BaseTranslator.py:55/137),**连 strict 模式都是无声的**;非严格模式更是直接
|
||
`return`。于是 `includes/ast/base.vp:168` 的 `enum ASTFlag:` 让整个 base.vp 的
|
||
声明(含顶层 `_init_ast`)一个都不注册,下游只报 `Undefined function: '_init_ast'`,
|
||
浪费了一轮排查。
|
||
|
||
fail-fast 依据:仓库有严格模式开关 `lib/constants/config.py:59 mode = "strict"`,
|
||
且既有多处同口径 `if _config_mode == "strict": raise`(本文件
|
||
`_RegisterSubModuleSha1:547`、`_build_stub_index:2443`、`_TryLoadStructFromStub:2539`
|
||
等)——本函数与它们一致,未放宽任何既有 fail-fast。
|
||
|
||
`FileNotFoundError` 例外:属"isfile 与 open 之间文件被删/被 `--clean` 清掉"的
|
||
竞态(先例见本文件 `_build_stub_index:2439-2441`),不是"源码解析失败",
|
||
故只告警、不触发 fail-fast。
|
||
"""
|
||
Site: str = _ParseFailureSite(Exc)
|
||
Message: str = (
|
||
f"声明解析失败 {FilePath}:{Site} — {type(Exc).__name__}: {Exc};{Consequence}"
|
||
)
|
||
if _config_mode == "strict" and not isinstance(Exc, FileNotFoundError):
|
||
raise ImportError(Message) from Exc
|
||
_vlog().warning(Message, "Exception")
|
||
|
||
|
||
def PrependInheritedBlock(Gen: object, ClassName: str, Block: list) -> bool:
|
||
"""把祖先字段块 `Block` 前置到 `Gen.class_members[ClassName]`(幂等,且**不删块内条目**)。
|
||
|
||
为什么单独成函数:这段判据是"继承段与 body 继承前缀一一对应"的唯一保障,必须可
|
||
独立验证(见本仓"按'表头是否已等于块'判幂等"的三条性质)。
|
||
|
||
判据与性质:
|
||
① **不删块内条目**。块里每一项都对应 body 的一个继承槽;只要块内某项被删,
|
||
被删项之后的继承字段的表内下标就会整体前移 k,而查询侧回退路径用
|
||
`base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型不同的槽。
|
||
旧实现按"名字已在整表内"过滤整块,正是这样删掉了**与子类自有字段重名**的
|
||
继承项(现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段
|
||
LastBuiltinsTypedefResult 下标 32→27,写进 i32 槽 → "指针隐式转整数已禁止")。
|
||
② **幂等**。本函数会被多个加载路径重复调用(_LoadModuleDeclarationsFromFile /
|
||
_LoadDeclarationsFromStubLlvm / _TryLoadClassMembersFromPyi 懒加载);判据是
|
||
"表头前 len(Block) 项的名字序列是否等于 Block 的名字序列"——相等即整块已前置,
|
||
直接返回 False(不再前置),避免 2026-08-10 那类重复字段
|
||
(BasicBlock: [Next, Next, Name, ...] → 与 struct elements 数量不匹配 → 偏移错乱)。
|
||
③ **同名并存**。子类自有字段与继承项同名时,两者都保留(定义端
|
||
HandlesClassDef 的 body 同样是"继承前缀 + 自有段",两侧槽数一致);
|
||
源码层面的这种重名已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下,
|
||
此处只是不让它把布局判据带偏。
|
||
|
||
已知残留:若此前只前置过**部分**祖先块(父类表当时较短),本次会整块再前置一次,
|
||
该前缀出现重复条目。调用点上方刚对父类做过 force 重载(块内容稳定),故该情形仅在
|
||
"父类表被中途改写"时出现,且重复条目会在成员槽失败报文的"名序"里直接可见。
|
||
|
||
返回 True 表示本次真的前置了。
|
||
"""
|
||
_Members: list = getattr(Gen, 'class_members', {}).get(ClassName) or []
|
||
_BlockNames: list = [nm for nm, _ in Block]
|
||
if not _BlockNames:
|
||
return False
|
||
_HeadNames: list = [nm for nm, _ in _Members[:len(_BlockNames)]]
|
||
if _HeadNames == _BlockNames:
|
||
return False
|
||
getattr(Gen, 'class_members')[ClassName] = list(Block) + list(_Members)
|
||
return True
|
||
|
||
|
||
class ImportHandle(BaseHandle):
|
||
_struct_Load_cache = {}
|
||
_pyi_cache = {}
|
||
_pyi_cache_dir = None
|
||
_project_root_cache = None
|
||
# 目录 os.walk 结果缓存(import 失败兜底查找用):目录 -> [(root, files)]。
|
||
# 编译进程内 SearchDirsForSubmodule(includes/LibraryPaths/工程目录)内容不变,
|
||
# 避免每次 import 解析失败都全量递归扫描目录树(PROF:import 热路径高频调用)。
|
||
_walk_cache: dict[str, list[tuple[str, list[str]]]] = {}
|
||
# 类级 stub 索引:class_name -> list of (full_name, struct_body, file_sha1, from_output)
|
||
# 一次扫描所有 .stub.ll 文件构建索引,避免 _TryLoadStructFromStub 每次调用都全量扫描
|
||
# (原 609 次调用 × ~100 stub 文件 × ~50 行 = 5.5M 正则匹配 → 一次扫描 ~5000 匹配)
|
||
_stub_index: dict[str, list[tuple[str, str, str, bool]]] = {}
|
||
# 已解析进索引的 stub 文件绝对路径集合(增量重建用)。
|
||
# 单次编译进程内 stub 只增不减(output 逐步填充),记录已扫描文件后,
|
||
# 签名变化只需增量解析新增文件,避免每编译完一个文件就全量重扫(原 847 次全量重建)。
|
||
_stub_index_scanned: set[str] = set()
|
||
# 类级缓存:跨 worker 任务共享(同一进程内,worker 单线程无竞争)。
|
||
# 键均为 (source_sha1, class_name),不同文件同名类(不同 sha1)不冲突。
|
||
# classify:_EmitExternalClassDeclLlvm 开头的 bases/decorator_list 分类遍历结果
|
||
# (与 Gen 无关的纯函数计算,同一 (sha1, class) 跨任务重复调用时跳过遍历)。
|
||
# decl:pyi 树中的 ClassDef 节点引用(pyi 树索引,pyi 树由 _pyi_cache 类级缓存
|
||
# 保证进程内唯一),避免 _TryLoadClassMembersFromPyi 跨任务反复线性扫描 body。
|
||
_extern_class_classify_cache: dict[tuple[str, str], tuple] = {}
|
||
_extern_class_decl_cache: dict[tuple[str, str], ast.ClassDef] = {}
|
||
# 类级 stub 函数签名缓存(_LookupStubFuncType):跨 worker 任务共享。
|
||
# 与 _stub_index/_stub_index_scanned 同模式:签名变化只增量解析新增 stub
|
||
# 文件(编译进程内 stub 只增不改),避免每次任务全量重扫 + 每文件
|
||
# os.path.isfile(Windows stat ~0.5ms/次,原 5840 次 ≈ 6.5s)。
|
||
_stub_func_cache: dict[str, tuple[str, str, bool, str]] = {}
|
||
_stub_func_cache_signature: tuple[int, int] | None = None
|
||
_stub_func_scanned: set[str] = set()
|
||
# stub 文件集合自上次建索引以来是否**可能**变化(写 *.stub.ll 的一方显式置脏)。
|
||
# 见 MarkStubSetDirty / _build_stub_index 的零 syscall 快路径:原实现每次
|
||
# _TryLoadStructFromStub 索引 miss 都要 2× os.path.isdir + 2× os.listdir 重新
|
||
# 拼文件集合(profile:_build_stub_index 被调 238k 次;nt._path_isdir 491s +
|
||
# nt.listdir 183s ≈ 总时长 30%),而"集合是否变化"完全可以由写入方免费告知。
|
||
_stub_set_dirty: bool = True
|
||
# 目录 → (listdir 时的目录 mtime_ns, 该次统计出的 *.stub.ll 数量)。
|
||
# 键为目录绝对路径:temp/ 与 output/ 分别计缓存。见 _CountStubFilesInDir。
|
||
_stub_dir_count_cache: dict[str, tuple[int, int]] = {}
|
||
|
||
@classmethod
|
||
def MarkStubSetDirty(cls) -> None:
|
||
"""标记「stub 文件集合可能已变化」——**所有写 *.stub.ll 的一方都必须调用**。
|
||
|
||
调用点多在 Phase2Translator:每个文件翻译入口(跨文件新增一律可见)、
|
||
写 temp/<sha1>.stub.ll 之后、注册 include 写 output/<sha1>.stub.ll 之后。
|
||
"""
|
||
ImportHandle._stub_set_dirty = True
|
||
|
||
def __init__(self, translator: "Translator") -> None:
|
||
super().__init__(translator)
|
||
# 实例级去重集合:每个 translator 独立跟踪已加载的 .pyi 和已发射的模块声明。
|
||
# 若作为类变量共享,main.py 编译时加载的 __nodes.py 会阻止 __parser.py 编译时
|
||
# 重新加载,导致函数未注册到 __parser.py 的 Gen.functions(NamedExpr bug 根因)。
|
||
self._loaded_pyi_paths: set[str] = set()
|
||
self._emitted_module_decls: set[tuple] = set()
|
||
# `_parse_llvm_type` 的结果 memo(键 = (类型串, source_sha1, create_structs))。
|
||
# 只缓存**与 Gen 状态无关**的结果,见 `_parse_llvm_type` 的判据;因此跨文件
|
||
# 复用是安全的(i8* 之类纯字面类型与哪个 Gen 无关),不随 `_reset_file_cache` 清空。
|
||
self._llvm_type_memo: dict[tuple, ir.Type] = {}
|
||
|
||
def _reset_file_cache(self) -> None:
|
||
self._struct_Load_cache.clear()
|
||
self._loaded_pyi_paths.clear()
|
||
self._emitted_module_decls.clear()
|
||
# stub 索引在文件间保留(同一 worker 进程内),但标记需检查是否过期
|
||
# 不清除 _stub_index / _stub_index_scanned,通过增量扫描校验新鲜度
|
||
|
||
def _find_project_root(self) -> str:
|
||
if self._project_root_cache is not None:
|
||
return self._project_root_cache
|
||
# 优先使用 translator 上显式设置的 _temp_dir(由 Phase2Translator._translate_file 传入),
|
||
# 这样可以正确处理 include 文件——其源文件目录不在项目根目录下,
|
||
# 从源文件目录向上搜索 temp/output 会失败并回退到错误的路径。
|
||
temp_dir_attr = getattr(self.Trans, '_temp_dir', None)
|
||
if temp_dir_attr and os.path.isdir(temp_dir_attr):
|
||
project_root = os.path.dirname(os.path.abspath(temp_dir_attr))
|
||
if os.path.isdir(os.path.join(project_root, 'temp')) and os.path.isdir(os.path.join(project_root, 'output')):
|
||
self._project_root_cache = project_root
|
||
return project_root
|
||
current_file = getattr(self.Trans, 'CurrentFile', '') or ''
|
||
if current_file:
|
||
current_dir = os.path.dirname(os.path.abspath(current_file))
|
||
search_dir = current_dir
|
||
for _ in range(10):
|
||
if os.path.isdir(os.path.join(search_dir, 'temp')) and os.path.isdir(os.path.join(search_dir, 'output')):
|
||
self._project_root_cache = search_dir
|
||
return search_dir
|
||
parent = os.path.dirname(search_dir)
|
||
if parent == search_dir:
|
||
break
|
||
search_dir = parent
|
||
# 回退到当前工作目录(编译运行时的项目根目录)
|
||
cwd = os.getcwd()
|
||
if os.path.isdir(os.path.join(cwd, 'temp')) and os.path.isdir(os.path.join(cwd, 'output')):
|
||
self._project_root_cache = cwd
|
||
return cwd
|
||
if current_file:
|
||
self._project_root_cache = current_dir
|
||
return current_dir
|
||
self._project_root_cache = cwd
|
||
return cwd
|
||
|
||
def _EmitImportDeclarationsLlvm(self, Node: ast.Import, Gen: LlvmGeneratorMixin) -> None:
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
for alias in Node.names:
|
||
name = alias.name
|
||
if name in ('c', 't'):
|
||
if alias.asname:
|
||
self.Trans.SymbolTable.import_aliases[alias.asname] = name
|
||
AliasInfo = CTypeInfo()
|
||
AliasInfo.IsModuleAlias = True
|
||
AliasInfo.ResolvedModule = name
|
||
self.Trans.SymbolTable.insert(alias.asname, AliasInfo)
|
||
continue
|
||
self.Trans._ImportedModules.add(name)
|
||
if alias.asname:
|
||
self.Trans.SymbolTable.import_aliases[alias.asname] = name
|
||
# 同时在符号表中注册模块别名,以便 CTypeInfo.FromNode 能解析
|
||
AliasInfo = CTypeInfo()
|
||
AliasInfo.IsModuleAlias = True
|
||
AliasInfo.ResolvedModule = name
|
||
self.Trans.SymbolTable.insert(alias.asname, AliasInfo)
|
||
current_module = self._get_current_module_name()
|
||
self._EmitModuleDeclarationsLlvm(name, Gen, register_module_name=current_module)
|
||
|
||
def _RegisterFromImportAliases(self, Node: ast.ImportFrom, Gen: LlvmGeneratorMixin, module: str) -> None:
|
||
"""Register 'from module import y as z' aliases after module declarations are Loaded"""
|
||
if not Node.names:
|
||
return
|
||
for alias in Node.names:
|
||
name = alias.name
|
||
asname = alias.asname
|
||
# For 'from X import Y' (no asname), register CDefine constants
|
||
# into _define_constants so they can be found by _HandleNameLlvm
|
||
if not asname or asname == name:
|
||
define_constants = vars(Gen).setdefault('_define_constants', {})
|
||
# Check if this name is a CDefine constant in SymbolTable
|
||
sym_info = self.Trans.SymbolTable.lookup(name)
|
||
if sym_info and sym_info.IsDefine and sym_info.DefineValue is not None:
|
||
if name not in define_constants:
|
||
define_constants[name] = sym_info.DefineValue
|
||
# Also check with module prefix
|
||
for prefix_key in [f"{module}.{name}", name]:
|
||
sym_info2 = self.Trans.SymbolTable.lookup(prefix_key)
|
||
if sym_info2 and sym_info2.IsDefine and sym_info2.DefineValue is not None:
|
||
if name not in define_constants:
|
||
define_constants[name] = sym_info2.DefineValue
|
||
break
|
||
# 治本修复:如果符号表中没有,回退查询 _all_define_constants
|
||
# _all_define_constants 由 Phase2Translator 从所有 .pyi 文件预提取,
|
||
# 包含跨模块导入的 CDefine 常量(如 FOREGROUND_GREEN、STD_OUTPUT_HANDLE)
|
||
if name not in define_constants:
|
||
all_dc: dict = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {}
|
||
if name in all_dc:
|
||
define_constants[name] = all_dc[name]
|
||
continue
|
||
if not hasattr(self.Trans, '_t_c_imported_names'):
|
||
self.Trans._t_c_imported_names = {}
|
||
self.Trans._t_c_imported_names[asname] = (module, name)
|
||
if name in Gen.functions:
|
||
Gen.functions[asname] = Gen.functions[name]
|
||
if name in Gen.structs:
|
||
Gen.structs[asname] = Gen.structs[name]
|
||
info = self.Trans.SymbolTable.lookup(name)
|
||
if info:
|
||
self.Trans.SymbolTable.insert(asname, info)
|
||
if name in Gen.variables:
|
||
Gen.variables[asname] = Gen.variables[name]
|
||
if name in Gen.global_vars:
|
||
Gen.global_vars[asname] = Gen.global_vars[name]
|
||
for func_name in list(Gen.functions.keys()):
|
||
if func_name.startswith(f'{name}.'):
|
||
method_suffix = func_name[len(name):]
|
||
Gen.functions[f'{asname}{method_suffix}'] = Gen.functions[func_name]
|
||
for key in list(self.Trans.SymbolTable.keys()):
|
||
if key.startswith(f'{name}.'):
|
||
suffix = key[len(name):]
|
||
self.Trans.SymbolTable.insert(f'{asname}{suffix}', self.Trans.SymbolTable[key])
|
||
for meta_dict in (Gen.class_members, Gen.class_member_defaults,
|
||
Gen.class_member_signeds, Gen.class_member_bitfields,
|
||
Gen.class_member_byteorders, Gen.class_member_bitoffsets,
|
||
Gen.class_methods):
|
||
if name in meta_dict:
|
||
meta_dict[asname] = meta_dict[name]
|
||
if name in Gen.class_vtable:
|
||
Gen.class_vtable.add(asname)
|
||
if name in Gen.class_packed:
|
||
Gen.class_packed.add(asname)
|
||
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
|
||
if name in struct_sha1_map:
|
||
struct_sha1_map[asname] = struct_sha1_map[name]
|
||
|
||
def _EmitImportFromDeclarationsLlvm(self, Node: ast.ImportFrom, Gen: LlvmGeneratorMixin) -> None:
|
||
module = Node.module if Node.module else ''
|
||
if module in ('c', 't'):
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(module)
|
||
if not hasattr(self.Trans, '_t_c_imported_names'):
|
||
self.Trans._t_c_imported_names = {}
|
||
# 注册 t/c 类型别名到 _t_type_symbols(CType 子类)
|
||
t_type_syms = self.Trans.SymbolTable._t_type_symbols
|
||
for alias in Node.names:
|
||
name = alias.name
|
||
asname = alias.asname or name
|
||
self.Trans._t_c_imported_names[asname] = (module, name)
|
||
# 仅注册 CType 子类(CEnum/CUnion/CStruct/REnum 等)
|
||
if module == 't':
|
||
cls = getattr(t, name, None)
|
||
if isinstance(cls, type) and issubclass(cls, t.CType):
|
||
t_type_syms[asname] = cls
|
||
return
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
current_module = self._get_current_module_name()
|
||
if Node.level and Node.level > 0:
|
||
current_file = getattr(self.Trans, 'CurrentFile', '') or ''
|
||
current_dir = os.path.dirname(os.path.abspath(current_file))
|
||
parent_dir = current_dir
|
||
for _ in range(Node.level - 1):
|
||
parent_dir = os.path.dirname(parent_dir)
|
||
if module:
|
||
SearchExtensions = ['.vpi', '.vp', '.pyi', '.py']
|
||
sub_path_base = os.path.join(parent_dir, module)
|
||
for ext in SearchExtensions:
|
||
candidate = sub_path_base + ext
|
||
if os.path.isfile(candidate):
|
||
self.Trans._ImportedModules.add(module)
|
||
self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module, register_module_name=current_module)
|
||
self._RegisterFromImportAliases(Node, Gen, module)
|
||
return
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
# 尝试完整模块名和短模块名
|
||
full_mod_name = f"{current_module}.{module}" if current_module else module
|
||
target_sha1 = ModuleSha1Map.get(full_mod_name) or ModuleSha1Map.get(module)
|
||
if target_sha1:
|
||
temp_dir = getattr(Gen, '_temp_dir', None)
|
||
if temp_dir:
|
||
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi")
|
||
if os.path.isfile(sha1_pyi):
|
||
self.Trans._ImportedModules.add(module)
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module, register_module_name=current_module)
|
||
self._RegisterFromImportAliases(Node, Gen, module)
|
||
return
|
||
else:
|
||
mod_dir = parent_dir
|
||
|
||
SearchExtensions = ['.vpi', '.vp', '.pyi', '.py']
|
||
pkg_name = ''
|
||
if not module:
|
||
# When 'from . import name' is used, Load the package's __init__
|
||
# to make package-level names (functions, classes, constants) available
|
||
pkg_name = self._LoadPackageInitForRelativeImport(mod_dir, Gen, current_module)
|
||
|
||
# Also try to Load each alias as a submodule
|
||
for alias in Node.names:
|
||
found_sub = False
|
||
for ext in SearchExtensions:
|
||
sub_path = os.path.join(mod_dir, alias.name + ext)
|
||
if os.path.isfile(sub_path):
|
||
self.Trans._ImportedModules.add(alias.name)
|
||
self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=current_module)
|
||
if alias.asname:
|
||
self.Trans.SymbolTable.import_aliases[alias.asname] = alias.name
|
||
found_sub = True
|
||
break
|
||
if not found_sub:
|
||
# SHA1 map fallback for submodule
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
sub_ModulePath = f"{pkg_name}.{alias.name}" if pkg_name else alias.name
|
||
target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name)
|
||
if target_sha1:
|
||
temp_dir = getattr(Gen, '_temp_dir', None)
|
||
if temp_dir:
|
||
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi")
|
||
if os.path.isfile(sha1_pyi):
|
||
self.Trans._ImportedModules.add(sub_ModulePath)
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=current_module)
|
||
if alias.asname:
|
||
self.Trans.SymbolTable.import_aliases[alias.asname] = alias.name
|
||
self._RegisterFromImportAliases(Node, Gen, module or pkg_name)
|
||
return
|
||
self.Trans._ImportedModules.add(module)
|
||
self._EmitModuleDeclarationsLlvm(module, Gen, register_module_name=current_module)
|
||
self._RegisterFromImportAliases(Node, Gen, module)
|
||
|
||
def _RegisterCrossModuleDocstrings(self, pyi_path: str, Gen: LlvmGeneratorMixin, module_prefix: str | None) -> None:
|
||
"""从 {sha1}.doc.json 加载跨模块 docstring 注册到 Gen._doc_strings。
|
||
|
||
- 长名 {module_prefix}.{symbol} 总是注册
|
||
- 短名 {symbol} 只在不存在时注册(避免覆盖本模块的)
|
||
- slice_level != 0(优化开启)时注册为 None
|
||
- 跨模块 docstring 在 _get_doc_ptr 访问时创建本地常量副本
|
||
"""
|
||
if not module_prefix:
|
||
return
|
||
basename: str = os.path.basename(pyi_path)
|
||
# 若传入 .vp/.py 文件,则从 _source_module_sig_files 查找对应的 {sha1}.vpi
|
||
if basename.endswith('.vp') or basename.endswith('.py'):
|
||
source_sig_files = getattr(self.Trans, '_source_module_sig_files', None)
|
||
if source_sig_files:
|
||
sig_path = source_sig_files.get(module_prefix)
|
||
if sig_path and os.path.isfile(sig_path):
|
||
pyi_path = sig_path
|
||
basename = os.path.basename(pyi_path)
|
||
else:
|
||
return
|
||
else:
|
||
return
|
||
if not (basename.endswith('.vpi') or basename.endswith('.pyi')):
|
||
return
|
||
sha1: str = basename[:-4] # 去掉 .vpi 或 .pyi 后缀(均为 4 字符)
|
||
shared_doc_meta = getattr(self.Trans, '_shared_doc_meta', None)
|
||
if not shared_doc_meta:
|
||
return
|
||
docs: dict[str, str] | None = shared_doc_meta.get(sha1)
|
||
if not docs:
|
||
return
|
||
trans = getattr(Gen, '_Trans', None)
|
||
slice_level: int = getattr(trans, 'SliceLevel', 3)
|
||
optimized: bool = slice_level != 0
|
||
for symbol, doc in docs.items():
|
||
long_key: str = f"{module_prefix}.{symbol}"
|
||
if optimized:
|
||
Gen._doc_strings[long_key] = None
|
||
if symbol not in Gen._doc_strings:
|
||
Gen._doc_strings[symbol] = None
|
||
else:
|
||
Gen._doc_strings[long_key] = doc
|
||
if symbol not in Gen._doc_strings:
|
||
Gen._doc_strings[symbol] = doc
|
||
|
||
def _LoadModuleDeclarationsFromFile(self, pyi_path: str, Gen: LlvmGeneratorMixin, module_name: str | None = None, register_module_name: str | None = None, actual_module_name: str | None = None, reexport_package: str | None = None) -> None:
|
||
if not pyi_path or not os.path.isfile(pyi_path):
|
||
return
|
||
# 去重:同一 .pyi 文件只加载一次(菱形依赖下避免重复 read+parse)
|
||
abs_pyi = os.path.abspath(pyi_path)
|
||
if abs_pyi in self._loaded_pyi_paths:
|
||
return
|
||
self._loaded_pyi_paths.add(abs_pyi)
|
||
# 注册跨模块 __doc__ docstring 到 Gen._doc_strings
|
||
self._RegisterCrossModuleDocstrings(pyi_path, Gen, register_module_name or module_name)
|
||
try:
|
||
with open(pyi_path, 'r', encoding='utf-8') as f:
|
||
ModuleCode = f.read()
|
||
# Viper 软关键字层(与 TPC 其余前端入口同一层):`.vp` 源码可含
|
||
# `enum X:` / `fn` 等语法糖,直接 vast.parse 会在糖行 SyntaxError →
|
||
# 被下面的 except 吞掉并 return → **该模块的全部声明(函数/类/全局)
|
||
# 一个都不注册**,调用方随后报 "Undefined function"
|
||
# (includes/ast/base.vp 的 `enum ASTFlag:` → 六处 `from .base import …`
|
||
# 全部拿不到 `_init_ast`,即本次现场)。`.vpi`/`.pyi` 无糖,展开是空操作。
|
||
ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), pyi_path)
|
||
except Exception as _e:
|
||
# 统一诊断口径(strict fail-fast / 非严格告警),必带文件+行号+异常+后果。
|
||
# 原实现"吞异常 + return"(strict 也只写进无人消费的 Trans.Warnings),
|
||
# 是本次 `Undefined function: '_init_ast'` 被伪装成下游症状的直接原因。
|
||
_ReportDeclarationParseFailure(
|
||
pyi_path, _e,
|
||
"该模块的全部声明(函数/类/全局/枚举)不会注册,"
|
||
"后续引用方可能表现为 Undefined function / Undefined struct")
|
||
return
|
||
|
||
# 计算源模块名(从 .pyi 文件路径推导)。
|
||
# 治本修复:module_name 参数可能来自 alias.name(如 `from stmts import Module`
|
||
# 中的 'Module' 是类名而非模块名),直接传给 _EmitExternalClassDeclLlvm 会导致
|
||
# ModuleSha1Map 查找失败 → _self_struct_sha1=None → 使用错误的同名 struct。
|
||
# 使用文件名推导的模块名(如 'stmts')作为 source_module_name 才是正确的。
|
||
# 对于 sha1 命名的 .pyi 文件(如 e64295c03b564a53.pyi),文件名是 sha1 而非
|
||
# 模块名,需回退到 actual_module_name 或 module_name。
|
||
file_module_name = os.path.splitext(os.path.basename(pyi_path))[0]
|
||
if len(file_module_name) == 16 and all(_c in '0123456789abcdef' for _c in file_module_name):
|
||
file_module_name = actual_module_name or module_name or file_module_name
|
||
|
||
for node in ModuleTree.body:
|
||
try:
|
||
if isinstance(node, ast.FunctionDef):
|
||
if node.name in ('va_start', 'va_arg', 'va_end'):
|
||
continue
|
||
effective_register = register_module_name or module_name
|
||
reexport_names = []
|
||
if module_name and module_name != file_module_name and module_name != effective_register:
|
||
reexport_names.append(module_name)
|
||
# When Loading a submodule for 'from .xxx import yyy' inside a package's __init__.py,
|
||
# re-export functions under the package name so 'pkg.func()' resolves correctly
|
||
if reexport_package and reexport_package != file_module_name and reexport_package not in reexport_names:
|
||
reexport_names.append(reexport_package)
|
||
self._EmitExternalFuncDeclLlvm(node, Gen, source_module_name=file_module_name, register_module_name=effective_register, reexport_module_names=reexport_names if reexport_names else None)
|
||
elif isinstance(node, ast.AnnAssign):
|
||
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=pyi_path)
|
||
elif isinstance(node, ast.Assign):
|
||
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=pyi_path)
|
||
elif isinstance(node, ast.ClassDef):
|
||
if hasattr(node, 'type_params') and node.type_params:
|
||
if not hasattr(self.Trans.ClassHandler, '_generic_class_templates'):
|
||
self.Trans.ClassHandler._generic_class_templates = {}
|
||
type_params = [tp.name for tp in node.type_params]
|
||
# 保留 prescan 阶段已注册的 sha1;若无则从 _ModuleSha1Map 查找,
|
||
# 避免覆盖带 sha1 的注册导致特化时 SHA1 前缀丢失
|
||
existing = self.Trans.ClassHandler._generic_class_templates.get(node.name)
|
||
template_sha1 = existing.get('sha1') if existing else None
|
||
if template_sha1 is None:
|
||
msm = getattr(self.Trans, '_ModuleSha1Map', None)
|
||
if msm:
|
||
for lookup_name in (file_module_name, module_name, actual_module_name):
|
||
if lookup_name and lookup_name in msm:
|
||
template_sha1 = msm[lookup_name]
|
||
break
|
||
self.Trans.ClassHandler._generic_class_templates[node.name] = {
|
||
'node': node,
|
||
'type_params': type_params,
|
||
'sha1': template_sha1,
|
||
}
|
||
# 治本修复:使用 file_module_name(源模块名)而非 module_name(可能是别名/类名)。
|
||
# 这样 _EmitExternalClassDeclLlvm 内的 ModuleSha1Map 查找才能命中,
|
||
# _EmitExternalFuncDeclLlvm 才能拿到正确的 source_module_name → 正确的 struct sha1。
|
||
# 注意:actual_module_name 保持原值(可能为 None),不回退到 module_name(可能是别名)
|
||
self._EmitExternalClassDeclLlvm(node, Gen, module_name=file_module_name, actual_module_name=actual_module_name)
|
||
elif isinstance(node, ast.ImportFrom):
|
||
if node.level > 0:
|
||
current_dir = os.path.dirname(os.path.abspath(pyi_path))
|
||
parent_dir = current_dir
|
||
for _ in range(node.level - 1):
|
||
parent_dir = os.path.dirname(parent_dir)
|
||
if node.module:
|
||
SearchExtensions = ['.vpi', '.vp', '.pyi', '.py']
|
||
sub_path_base = os.path.join(parent_dir, node.module)
|
||
found_sub = False
|
||
# 治本修复:用包目录名(如 'json')而非 register_module_name(当前编译模块名,如 '_dict')。
|
||
# 当 _dict.py 导入 json 包,递归处理 json/__init__.py 中的 `from .__parser import parse` 时,
|
||
# register_module_name='_dict' 会导致 actual_module='_dict.__parser',
|
||
# 使 _mangle_func_name 查找失败回退到 self.module_sha1,生成错误的 mangled name。
|
||
# 包名推导优先级:
|
||
# 1. module_name(如果它在 ModuleSha1Map 中,说明是已注册的包名,如 'json')
|
||
# — 处理 sha1 命名的 .pyi 文件(parent_dir 是 temp 目录时)
|
||
# 2. os.path.basename(parent_dir)(原始路径推导,如 'json')
|
||
if module_name and module_name in Gen.ModuleSha1Map:
|
||
pkg_basename = module_name
|
||
else:
|
||
pkg_basename = os.path.basename(parent_dir)
|
||
for ext in SearchExtensions:
|
||
candidate = sub_path_base + ext
|
||
if os.path.isfile(candidate):
|
||
actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module
|
||
self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen)
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(actual_module)
|
||
for alias in node.names:
|
||
if alias.name == '*':
|
||
self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
|
||
else:
|
||
self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=reexport_package or module_name)
|
||
found_sub = True
|
||
break
|
||
# SHA1 map 回退
|
||
if not found_sub:
|
||
actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module)
|
||
if target_sha1:
|
||
temp_dir = getattr(Gen, '_temp_dir', None)
|
||
if temp_dir:
|
||
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi")
|
||
if os.path.isfile(sha1_pyi):
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(actual_module)
|
||
for alias in node.names:
|
||
if alias.name == '*':
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
|
||
else:
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=reexport_package or module_name)
|
||
else:
|
||
mod_dir = parent_dir
|
||
SearchExtensions = ['.vpi', '.vp', '.pyi', '.py']
|
||
# Load __init__.py to make package-level names available
|
||
self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name)
|
||
# 治本修复:用包目录名(如 'json')而非 register_module_name(当前编译模块名,如 '_dict')。
|
||
# 同 if node.module 分支的修复逻辑。
|
||
if module_name and module_name in Gen.ModuleSha1Map:
|
||
pkg_basename = module_name
|
||
else:
|
||
pkg_basename = os.path.basename(mod_dir)
|
||
for alias in node.names:
|
||
found_alias = False
|
||
for ext in SearchExtensions:
|
||
sub_path = os.path.join(mod_dir, alias.name + ext)
|
||
if os.path.isfile(sub_path):
|
||
sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(sub_ModulePath)
|
||
if alias.name == '*':
|
||
self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
|
||
else:
|
||
self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
|
||
found_alias = True
|
||
break
|
||
# SHA1 map 回退
|
||
if not found_alias:
|
||
sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name)
|
||
if target_sha1:
|
||
temp_dir = getattr(Gen, '_temp_dir', None)
|
||
if temp_dir:
|
||
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi")
|
||
if os.path.isfile(sha1_pyi):
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(sub_ModulePath)
|
||
if alias.name == '*':
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
|
||
else:
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
|
||
except Exception as _e:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
if isinstance(_e, _FFE):
|
||
# fail-fast 一律上抛(本仓既定口径:FailFastError 不被吞,见
|
||
# HandlesBase.py 的说明)。原实现把它与其它异常一起吞成一行
|
||
# "忽略节点 … 异常" WARN 并 continue ⇒ 该节点(如某个 ClassDef 的
|
||
# 全部方法声明)被整条静默丢弃,创建中途失败的**半注册名字**留到
|
||
# 下一轮才以"函数声明名字悬空占用"暴露,真因与现场彻底脱节
|
||
# (现场:50741ad4b4b0934d.LLVMTypeCore.__init__)。
|
||
raise
|
||
_vlog().warning(
|
||
f"HandlesImports[_LoadModuleDeclarationsFromFile]: 忽略节点 "
|
||
f"{type(node).__name__} {getattr(node, 'name', '?')} 异常: {_e}",
|
||
exc_info=_e
|
||
)
|
||
continue
|
||
|
||
def _get_current_module_name(self) -> str | None:
|
||
current_file = getattr(self.Trans, 'CurrentFile', '') or ''
|
||
if current_file:
|
||
return os.path.splitext(os.path.basename(current_file))[0]
|
||
return None
|
||
|
||
def _RegisterSubModuleSha1(self, candidate_path: str, actual_module: str, short_module: str, Gen: LlvmGeneratorMixin) -> None:
|
||
"""Register a submodule's SHA1 in ModuleSha1Map so cross-module class references work"""
|
||
try:
|
||
with open(candidate_path, 'r', encoding='utf-8') as f:
|
||
sub_content = f.read()
|
||
sub_sha1 = hashlib.sha1(sub_content.encode('utf-8')).hexdigest()[:16]
|
||
if hasattr(Gen, 'ModuleSha1Map'):
|
||
Gen.ModuleSha1Map[actual_module] = sub_sha1
|
||
if short_module and short_module not in Gen.ModuleSha1Map:
|
||
Gen.ModuleSha1Map[short_module] = sub_sha1
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
|
||
def _LoadPackageInitForRelativeImport(self, mod_dir: str, Gen: LlvmGeneratorMixin, register_module_name: str | None) -> str:
|
||
"""Load __init__.py from the package directory for 'from . import name' resolution.
|
||
This makes package-level names (functions, classes, constants) available.
|
||
Returns the package name."""
|
||
pkg_name = os.path.basename(mod_dir)
|
||
init_Loaded = False
|
||
|
||
# 跳过当前正在编译的文件,避免重复加载导致 class_members 重复
|
||
current_sha1 = getattr(Gen, 'module_sha1', None)
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
pkg_sha1 = ModuleSha1Map.get(pkg_name)
|
||
if pkg_sha1 and pkg_sha1 == current_sha1:
|
||
return pkg_name
|
||
|
||
# Try SHA1 map first (correct mangled names from stub)
|
||
if pkg_sha1:
|
||
temp_dir = getattr(Gen, '_temp_dir', None)
|
||
if temp_dir:
|
||
sha1_pyi = os.path.join(temp_dir, f"{pkg_sha1}.vpi")
|
||
if os.path.isfile(sha1_pyi):
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=pkg_name, register_module_name=register_module_name)
|
||
init_Loaded = True
|
||
|
||
# Fallback: try Loading __init__.py directly from the package directory
|
||
if not init_Loaded:
|
||
for ext in ['.vpi', '.vp', '.pyi', '.py']:
|
||
init_path = os.path.join(mod_dir, '__init__' + ext)
|
||
if os.path.isfile(init_path):
|
||
self._LoadModuleDeclarationsFromFile(init_path, Gen, module_name=pkg_name, register_module_name=register_module_name)
|
||
init_Loaded = True
|
||
break
|
||
|
||
return pkg_name
|
||
|
||
def _EmitModuleDeclarationsLlvm(self, module_name: str, Gen: LlvmGeneratorMixin, register_module_name: str | None = None) -> None:
|
||
# 去重:同一模块声明只发射一次(避免多个 import 同一模块时重复加载)
|
||
decl_key = (module_name, register_module_name)
|
||
if decl_key in self._emitted_module_decls:
|
||
return
|
||
self._emitted_module_decls.add(decl_key)
|
||
if module_name in ('pyzlib',):
|
||
source_sig_files = getattr(self.Trans, '_source_module_sig_files', None)
|
||
ModulePathBase = module_name.replace('.', os.sep)
|
||
PackageInitPathBase = module_name.replace('.', os.sep) + os.sep + '__init__'
|
||
ProjectRoot = os.path.dirname(os.path.abspath(getattr(self.Trans, 'CurrentFile', '') or '')) or os.getcwd()
|
||
|
||
HandlesFile = os.path.abspath(inspect.getfile(self.__class__))
|
||
TransPyCRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(HandlesFile))))
|
||
|
||
FullModulePath = None
|
||
# includes 只认项目(project.vpj includes → LibraryPaths)目录,禁止回退仓库硬编码
|
||
# TransPyCRoot/includes:仓库副本缺失时回退会造成声明解析时序错乱(memhub 未注册→i32)。
|
||
SearchDirs = list(self.Trans.LibraryPaths)
|
||
|
||
for LibPath in SearchDirs:
|
||
SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath
|
||
# 优先尝试 .vpi,回退 .pyi
|
||
for stub_ext in ('.vpi', '.pyi'):
|
||
CandidatePath = os.path.join(SearchPath, ModulePathBase + stub_ext)
|
||
if os.path.isfile(CandidatePath):
|
||
FullModulePath = CandidatePath
|
||
break
|
||
CandidatePath = os.path.join(SearchPath, PackageInitPathBase + stub_ext)
|
||
if os.path.isfile(CandidatePath):
|
||
FullModulePath = CandidatePath
|
||
break
|
||
if FullModulePath:
|
||
break
|
||
ModuleParts = ModulePathBase.split(os.sep)
|
||
if len(ModuleParts) > 1:
|
||
LibPathBasename = os.path.basename(LibPath.rstrip('/\\'))
|
||
if LibPathBasename == ModuleParts[0]:
|
||
RemainingPath = os.sep.join(ModuleParts[1:])
|
||
for stub_ext in ('.vpi', '.pyi'):
|
||
CandidatePath = os.path.join(SearchPath, RemainingPath + stub_ext)
|
||
if os.path.isfile(CandidatePath):
|
||
FullModulePath = CandidatePath
|
||
break
|
||
if FullModulePath:
|
||
break
|
||
if not FullModulePath:
|
||
ModulePathPyBase = module_name.replace('.', os.sep)
|
||
PackageInitPathPyBase = module_name.replace('.', os.sep) + os.sep + '__init__'
|
||
SearchDirsForPy = list(self.Trans.LibraryPaths)
|
||
for LibPath in SearchDirsForPy:
|
||
SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath
|
||
# 优先尝试 .vp,回退 .py
|
||
for src_ext in ('.vp', '.py'):
|
||
CandidatePath = os.path.join(SearchPath, ModulePathPyBase + src_ext)
|
||
if os.path.isfile(CandidatePath):
|
||
FullModulePath = CandidatePath
|
||
break
|
||
CandidatePath = os.path.join(SearchPath, PackageInitPathPyBase + src_ext)
|
||
if os.path.isfile(CandidatePath):
|
||
FullModulePath = CandidatePath
|
||
break
|
||
if FullModulePath:
|
||
break
|
||
if not FullModulePath or not os.path.isfile(FullModulePath):
|
||
source_sig_files = getattr(self.Trans, '_source_module_sig_files', None)
|
||
if source_sig_files:
|
||
sig_path = source_sig_files.get(module_name)
|
||
if sig_path and os.path.isfile(sig_path):
|
||
FullModulePath = sig_path
|
||
else:
|
||
for key, val in source_sig_files.items():
|
||
if key.endswith('.' + module_name) or key == module_name:
|
||
FullModulePath = val
|
||
break
|
||
if not FullModulePath or not os.path.isfile(FullModulePath):
|
||
if module_name in ('pyzlib', 'zdeflate', 'zinflate', 'zchecksum', 'zdef', 'zhuff'):
|
||
pass
|
||
if not FullModulePath:
|
||
CurrentFile = getattr(self.Trans, 'CurrentFile', None) or ''
|
||
if CurrentFile:
|
||
CurrentFileDir = os.path.dirname(os.path.abspath(CurrentFile))
|
||
SearchFromDirs = [CurrentFileDir, os.getcwd()]
|
||
else:
|
||
SearchFromDirs = [os.getcwd()]
|
||
SearchDirsForSubmodule = SearchFromDirs + list(self.Trans.LibraryPaths)
|
||
for SearchPath in SearchDirsForSubmodule:
|
||
if not os.path.isdir(SearchPath):
|
||
continue
|
||
# 多级模块名(含 '.')与 basename 文件列表(files 无路径)
|
||
# 永不匹配 `module_name + ext in files`,walk 纯属浪费,跳过。
|
||
if '.' in module_name:
|
||
continue
|
||
# 目录 walk 结果缓存(编译进程内目录内容不变):import 热路径
|
||
# 高频解析失败时避免重复递归扫描 includes/工程目录(PROF 热点)
|
||
walk_entries: list[tuple[str, list[str]]] = ImportHandle._walk_cache.get(SearchPath)
|
||
if walk_entries is None:
|
||
walk_entries = [(r, fs) for r, dirs, fs in os.walk(SearchPath) if '__pycache__' not in r]
|
||
ImportHandle._walk_cache[SearchPath] = walk_entries
|
||
for root, files in walk_entries:
|
||
for src_ext in ('.vp', '.py', '.vpi', '.pyi'):
|
||
if module_name + src_ext in files:
|
||
FullModulePath = os.path.join(root, module_name + src_ext)
|
||
break
|
||
if FullModulePath:
|
||
break
|
||
if FullModulePath:
|
||
break
|
||
# fail-fast:模块在所有来源都"完全未知"时立即报错,暴露 import 路径错误
|
||
# (如目录重组后未更新的旧路径)。之前静默容忍导致引用方跨模块 mangled
|
||
# 前缀 fallback 当前模块 sha1,链接期才暴露 undefined symbol。
|
||
# 注意:文件系统找不到 ≠ 模块不存在——TPV 自举工程 includes 为合并形态,
|
||
# 源码模块(如 lib.Stub.StubMerger 包)不提供裸 .vp 文件,声明由
|
||
# ModuleSha1Map/_source_module_sig_files 定位 sha1 → temp/*.stub.ll 加载,
|
||
# 属正常路径;只有模块在编译上下文完全未注册(无 sha1)才算真正不存在。
|
||
if not FullModulePath or not os.path.isfile(FullModulePath):
|
||
_known_sha1: str | None = None
|
||
_sig_files = getattr(self.Trans, '_source_module_sig_files', None)
|
||
if _sig_files:
|
||
for _key, _val in _sig_files.items():
|
||
if _key == module_name or _key.endswith('.' + module_name):
|
||
_known_sha1 = os.path.basename(_val).replace('.vpi', '').replace('.pyi', '')
|
||
break
|
||
if not _known_sha1:
|
||
_sha_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', None) or {}
|
||
_known_sha1 = _sha_map.get(module_name)
|
||
if not _known_sha1:
|
||
raise ImportError(
|
||
f"无法解析 import 模块 '{module_name}': "
|
||
f"在 includes/LibraryPaths/工程目录均未找到对应 .vp/.py/.vpi/.pyi 文件,"
|
||
f"且编译上下文无该模块的 sha1 注册(模块完全未知)")
|
||
# 注册跨模块 __doc__ docstring(module_name 是被导入模块名)
|
||
if FullModulePath:
|
||
self._RegisterCrossModuleDocstrings(FullModulePath, Gen, module_name)
|
||
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
|
||
if module_name == 'pyzlib':
|
||
pass
|
||
try:
|
||
with open(FullModulePath, 'r', encoding='utf-8') as f:
|
||
ModuleCode = f.read()
|
||
# 同上:`.vp` 源码须先过 Viper 软关键字层再 vast.parse(否则含
|
||
# `enum X:` / `fn` 的模块声明整体丢失,落到 except 的 stub 兜底)。
|
||
ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), FullModulePath)
|
||
if module_name == 'pyzlib':
|
||
func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)]
|
||
has_functions_or_classes = False
|
||
for node in ModuleTree.body:
|
||
if module_name == 'pyzlib':
|
||
node_type = type(node).__name__
|
||
name = getattr(node, 'name', getattr(node, 'attr', '?'))
|
||
try:
|
||
if isinstance(node, ast.FunctionDef):
|
||
if node.name in ('va_start', 'va_arg', 'va_end'):
|
||
continue
|
||
effective_register = register_module_name or module_name
|
||
self._EmitExternalFuncDeclLlvm(node, Gen, source_module_name=module_name, register_module_name=effective_register)
|
||
has_functions_or_classes = True
|
||
elif isinstance(node, ast.AnnAssign):
|
||
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=FullModulePath)
|
||
has_functions_or_classes = True
|
||
elif isinstance(node, ast.Assign):
|
||
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=FullModulePath)
|
||
has_functions_or_classes = True
|
||
elif isinstance(node, ast.ClassDef):
|
||
if hasattr(node, 'type_params') and node.type_params:
|
||
if not hasattr(self.Trans.ClassHandler, '_generic_class_templates'):
|
||
self.Trans.ClassHandler._generic_class_templates = {}
|
||
type_params = [tp.name for tp in node.type_params]
|
||
# 保留 prescan 阶段已注册的 sha1;若无则从 _ModuleSha1Map 查找,
|
||
# 避免覆盖带 sha1 的注册导致特化时 SHA1 前缀丢失
|
||
existing = self.Trans.ClassHandler._generic_class_templates.get(node.name)
|
||
template_sha1 = existing.get('sha1') if existing else None
|
||
if template_sha1 is None:
|
||
msm = getattr(self.Trans, '_ModuleSha1Map', None)
|
||
if msm and module_name in msm:
|
||
template_sha1 = msm[module_name]
|
||
self.Trans.ClassHandler._generic_class_templates[node.name] = {
|
||
'node': node,
|
||
'type_params': type_params,
|
||
'sha1': template_sha1,
|
||
}
|
||
self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name)
|
||
has_functions_or_classes = True
|
||
elif isinstance(node, ast.ImportFrom):
|
||
if node.level > 0:
|
||
current_dir = os.path.dirname(os.path.abspath(FullModulePath))
|
||
parent_dir = current_dir
|
||
for _ in range(node.level - 1):
|
||
parent_dir = os.path.dirname(parent_dir)
|
||
if node.module:
|
||
SearchExtensions = ['.vpi', '.vp', '.pyi', '.py']
|
||
sub_path_base = os.path.join(parent_dir, node.module)
|
||
found_sub = False
|
||
for ext in SearchExtensions:
|
||
candidate = sub_path_base + ext
|
||
if os.path.isfile(candidate):
|
||
actual_module = f"{module_name}.{node.module}" if module_name else node.module
|
||
self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen)
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(actual_module)
|
||
for alias in node.names:
|
||
if alias.name == '*':
|
||
self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name)
|
||
else:
|
||
self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=module_name)
|
||
has_functions_or_classes = True
|
||
found_sub = True
|
||
break
|
||
# SHA1 map 回退:当文件查找失败时,从 SHA1 映射中查找子模块 stub
|
||
if not found_sub:
|
||
actual_module = f"{module_name}.{node.module}" if module_name else node.module
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
# 尝试完整模块名和短模块名
|
||
target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module)
|
||
if target_sha1:
|
||
temp_dir = getattr(Gen, '_temp_dir', None)
|
||
if temp_dir:
|
||
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi")
|
||
if os.path.isfile(sha1_pyi):
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(actual_module)
|
||
for alias in node.names:
|
||
if alias.name == '*':
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name)
|
||
else:
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=module_name)
|
||
has_functions_or_classes = True
|
||
else:
|
||
mod_dir = parent_dir
|
||
SearchExtensions = ['.vpi', '.vp', '.pyi', '.py']
|
||
# Load __init__.py to make package-level names available
|
||
self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name or module_name)
|
||
for alias in node.names:
|
||
found_alias = False
|
||
for ext in SearchExtensions:
|
||
sub_path = os.path.join(mod_dir, alias.name + ext)
|
||
if os.path.isfile(sub_path):
|
||
sub_ModulePath = f"{module_name}.{alias.name}" if module_name else alias.name
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(sub_ModulePath)
|
||
if alias.name == '*':
|
||
self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name)
|
||
else:
|
||
self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=module_name)
|
||
has_functions_or_classes = True
|
||
found_alias = True
|
||
break
|
||
# SHA1 map 回退
|
||
if not found_alias:
|
||
sub_ModulePath = f"{module_name}.{alias.name}" if module_name else alias.name
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name)
|
||
if target_sha1:
|
||
temp_dir = getattr(Gen, '_temp_dir', None)
|
||
if temp_dir:
|
||
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi")
|
||
if os.path.isfile(sha1_pyi):
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(sub_ModulePath)
|
||
if alias.name == '*':
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name)
|
||
else:
|
||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=module_name)
|
||
has_functions_or_classes = True
|
||
except RecursionError:
|
||
# 家族口径(HandlesBase.py:1060-1068):RecursionError 不是"可忽略异常",
|
||
# 而是类型解析/声明发射链**成环**的真缺陷信号。此前它被本处
|
||
# `except Exception` 吞成一行 `WARN: 忽略异常 maximum recursion depth exceeded`
|
||
# + 声明静默退化(现场与 :2985 深探针记录一致:`match.MatchOr.patterns`、
|
||
# `stmts.Interactive.body` 等"首字段即指针/容器"的类会触发
|
||
# `_TryLoadClassMembersFromPyi ↔ _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub`
|
||
# 环)。此处 fail-fast 上抛(保留原始栈),暴露上游根因而非掩盖。
|
||
raise
|
||
except Exception as e:
|
||
# NoneType 异常是数据缺失的正常兜底,静默不 WARN(干扰太多)
|
||
if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)):
|
||
if module_name == 'pyzlib':
|
||
node_type = type(node).__name__
|
||
name = getattr(node, 'name', getattr(node, 'attr', '?'))
|
||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
||
continue
|
||
if not has_functions_or_classes and module_name:
|
||
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
|
||
elif 'includes' in FullModulePath and module_name:
|
||
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
|
||
except Exception as e:
|
||
# 同族口径:读/parse 失败必须显式(文件+行号+异常+后果),不允许静默降级。
|
||
# 非严格模式仍保留既有 stub 兜底(StubGen 产物声明仍可用);strict 模式
|
||
# 由 _ReportDeclarationParseFailure 直接 fail-fast,与 :412 主点一致。
|
||
#
|
||
# **限定条件**:只有"确实有源码文件却读/解析失败"才算声明解析失败。本函数
|
||
# 的 `FullModulePath` 可为 None——TPV 自举工程 includes 为合并形态,源码模块
|
||
# (如 lib.Stub.StubMerger 包)不提供裸 .vp 文件,声明由上方
|
||
# `_LoadDeclarationsFromStubLlvm` 从 temp/*.stub.ll 加载,属**正常路径**
|
||
# (见本函数 :750 附近的长注释);此时 `open(None)` 的 TypeError 与"文件恰好
|
||
# 不在"都不应触发 fail-fast。
|
||
if FullModulePath and os.path.isfile(FullModulePath):
|
||
_ReportDeclarationParseFailure(
|
||
FullModulePath, e,
|
||
"该模块的源码声明全部未注册,"
|
||
"仅能依赖 stub 兜底(非严格模式);声明可能与源码不一致")
|
||
if module_name:
|
||
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
|
||
|
||
def _AnnotationStructName(self, annotation: ast.AST) -> str | None:
|
||
"""从注解里提取「指针指向的类」短名(递归,0 判定返回 None)
|
||
|
||
覆盖形态:`X` / `mod.X` / `rawptr[X]` / `t.CPtr[X]` / 联合 `A | B`(两翼都试)。
|
||
存储类与限定符标记(`t.CExtern` / `t.CExport` / `t.CPtr` 等)不算类名,
|
||
由 `_MARKER_ANNOT_NAMES` 过滤;返回的是**短名**,调用方再解析成
|
||
`Gen.structs` 真实键。
|
||
"""
|
||
if annotation is None:
|
||
return None
|
||
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
|
||
_left: str | None = self._AnnotationStructName(annotation.left)
|
||
if _left:
|
||
return _left
|
||
return self._AnnotationStructName(annotation.right)
|
||
if isinstance(annotation, ast.Subscript):
|
||
_base: ast.AST = annotation.value
|
||
_bn: str = ''
|
||
if isinstance(_base, ast.Name):
|
||
_bn = _base.id
|
||
elif isinstance(_base, ast.Attribute):
|
||
_bn = _base.attr
|
||
if _bn in ('rawptr', 'CPtr', 'CArrayPtr'):
|
||
_sl: ast.AST = annotation.slice
|
||
if hasattr(ast, 'Index') and isinstance(_sl, ast.Index):
|
||
_sl = _sl.value
|
||
return self._AnnotationStructName(_sl)
|
||
return None
|
||
if isinstance(annotation, ast.Name):
|
||
if annotation.id in _MARKER_ANNOT_NAMES:
|
||
return None
|
||
return annotation.id
|
||
if isinstance(annotation, ast.Attribute):
|
||
# `mod.Class`:mod 为 t/c 时是内建标记/内建类型名,不作为类的键
|
||
if isinstance(annotation.value, ast.Name) and annotation.value.id in ('t', 'c'):
|
||
return None
|
||
if annotation.attr in _MARKER_ANNOT_NAMES:
|
||
return None
|
||
return annotation.attr
|
||
return None
|
||
|
||
def _ResolveAnnotationStructKey(self, Gen: LlvmGeneratorMixin, name: str) -> str | None:
|
||
"""把注解里的类短名解析为 `Gen.structs` 的真实键(0 命中返回 None)
|
||
|
||
三条路径与类字段侧的 `rawptr[裸结构]` 登记保持一致:裸名直查 /
|
||
`class_sha1_map` 加前缀 / 全量短名模糊扫描(跨模块类在
|
||
`class_sha1_map` 尚未登记该短名时靠这条兜住)。
|
||
"""
|
||
if not name:
|
||
return None
|
||
if name in Gen.structs:
|
||
return name
|
||
_sha: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(name)
|
||
if _sha and f'{_sha}.{name}' in Gen.structs:
|
||
return f'{_sha}.{name}'
|
||
for _k in Gen.structs:
|
||
_short: str = _k.split('.')[-1] if '.' in _k else _k
|
||
if _short == name:
|
||
return _k
|
||
return None
|
||
|
||
def _EmitExternalGlobalDeclLlvm(self, Node: ast.AnnAssign | ast.Assign, Gen: LlvmGeneratorMixin, module_name: str | None = None, ModulePath: str | None = None) -> None:
|
||
"""Emit external global variable declaration from imported module"""
|
||
if isinstance(Node, ast.AnnAssign) and isinstance(Node.target, ast.Name):
|
||
VarName = Node.target.id
|
||
# 不再跳过 _ 开头的全局变量,跨模块引用需要声明它们
|
||
var_type = ir.IntType(32)
|
||
IsPtr = False
|
||
is_string_val = False
|
||
TypeInfo = None
|
||
# 【跨模块模块级变量的**类登记**】`CTypeInfo.FromNode` 对
|
||
# `X | t.CPtr` / `rawptr[X]` 注解只给出指针占位(rawptr[X] 折叠成 i8*),
|
||
# 且本函数**只算类型、不登记变量类** → 消费侧 `Mod.Var.Field` 链在
|
||
# `_get_var_class` 里解析为空:StructGenMixin.vp:139 "属性访问未能解析出
|
||
# 目标类型/成员"、CallGenLeafKit.vp:589 / ClassGenVTable.vp:99 误报
|
||
# Unknown module(factory.PermPool.alloc)。这里对齐 HandlesAnnAssign
|
||
# 变量侧补偿:从注解找回真实结构体键并登记;仅当缺失或指针占位时覆盖。
|
||
# 注意:必须放在下面的「已存在则 return」**之前**,否则全局量先由别的
|
||
# 路径声明(如 stub 文本 i8*)时这里永远不执行。
|
||
_eg_tn: str | None = self._AnnotationStructName(Node.annotation)
|
||
if _eg_tn:
|
||
# 只在能落到 Gen.structs 真实键时登记(避免把 sizet/CPtr 等标量名当类)
|
||
_eg_tk: str | None = self._ResolveAnnotationStructKey(Gen, _eg_tn)
|
||
if _eg_tk:
|
||
_eg_cur: str | None = Gen.var_struct_class.get(VarName)
|
||
if (not _eg_cur or _eg_cur == 'rawptr' or _eg_cur.endswith('.rawptr')
|
||
or _eg_cur.startswith('rawptr[')):
|
||
Gen.var_struct_class[VarName] = _eg_tk
|
||
if getattr(Gen, 'global_struct_class', None) is not None:
|
||
Gen.global_struct_class[VarName] = _eg_tk
|
||
if VarName in Gen.module.globals:
|
||
existing = Gen.module.globals[VarName]
|
||
if isinstance(existing, ir.GlobalVariable):
|
||
return
|
||
if Node.annotation:
|
||
parse_result = ParseListAnnotation(Node.annotation)
|
||
if parse_result and not parse_result.is_pointer:
|
||
# list[type, count] 数组声明
|
||
elem_type_node = parse_result.elem_type_node
|
||
count_node = parse_result.count_node
|
||
ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable)
|
||
if ElemTypeInfo:
|
||
elem_type = Gen._ctype_to_llvm(ElemTypeInfo)
|
||
if isinstance(elem_type, ir.VoidType):
|
||
elem_type = ir.IntType(8)
|
||
if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int):
|
||
var_type = ir.ArrayType(elem_type, count_node.value)
|
||
elif isinstance(count_node, ast.Constant) and count_node.value is None:
|
||
var_type = ir.ArrayType(elem_type, 0)
|
||
else:
|
||
var_type = Gen._ctype_to_llvm(ElemTypeInfo)
|
||
IsPtr = False
|
||
else:
|
||
TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
|
||
if TypeInfo:
|
||
var_type = Gen._ctype_to_llvm(TypeInfo)
|
||
IsPtr = TypeInfo.IsPtr
|
||
if isinstance(var_type, ir.IdentifiedStructType) and (var_type.elements is None or len(var_type.elements) == 0):
|
||
var_type = ir.IntType(32)
|
||
if Node.value and ModulePath and IsPtr and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str):
|
||
str_val = Node.value.value + '\x00'
|
||
str_bytes = str_val.encode('utf-8')
|
||
arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes))
|
||
str_gv_name = VarName + '_str'
|
||
if str_gv_name not in Gen.module.globals:
|
||
str_gv = ir.GlobalVariable(Gen.module, arr_type, name=str_gv_name)
|
||
str_gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
|
||
str_gv.linkage = 'internal'
|
||
ptr_type = ir.PointerType(ir.IntType(8))
|
||
if VarName not in Gen.module.globals:
|
||
ptr_gv = ir.GlobalVariable(Gen.module, ptr_type, name=VarName)
|
||
str_gv = Gen.module.globals[str_gv_name]
|
||
ptr_gv.initializer = ir.Constant.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)])
|
||
ptr_gv.linkage = 'internal'
|
||
Gen.variables[VarName] = None
|
||
return
|
||
|
||
# 检查是否是 CDefine 常量或 CTypedef 类型别名
|
||
IsCDefine = False
|
||
IsCTypedef = False
|
||
if Node.annotation:
|
||
if isinstance(Node.annotation, ast.Attribute):
|
||
if getattr(Node.annotation.value, 'id', None) == 't' and Node.annotation.attr == 'CDefine':
|
||
IsCDefine = True
|
||
elif getattr(Node.annotation.value, 'id', None) == 't' and Node.annotation.attr == 'CTypedef':
|
||
IsCTypedef = True
|
||
elif isinstance(Node.annotation, ast.Name):
|
||
if Node.annotation.id == 'CDefine':
|
||
IsCDefine = True
|
||
elif Node.annotation.id == 'CTypedef':
|
||
IsCTypedef = True
|
||
|
||
if IsCTypedef:
|
||
# 检查符号是否已存在且已正确解析(OriginalType 已设置),避免覆盖
|
||
existing_sym = self.Trans.SymbolTable.lookup(VarName) if hasattr(self.Trans.SymbolTable, 'lookup') else None
|
||
if existing_sym and isinstance(existing_sym, CTypeInfo) and existing_sym.IsTypedef and existing_sym.OriginalType:
|
||
return
|
||
if Node.value:
|
||
ValueTypeInfo = None
|
||
if isinstance(Node.value, ast.BinOp) and isinstance(Node.value.op, ast.BitOr):
|
||
ValueTypeInfo = self.Trans.TypeMergeHandler.MergeTypes(Node.value)
|
||
else:
|
||
ValueTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.value)
|
||
if isinstance(ValueTypeInfo, CTypeInfo) and (ValueTypeInfo.BaseType or ValueTypeInfo.PtrCount > 0):
|
||
# GetCTypeInfo 对 t.CInt 等 Attribute 返回的 BaseType 可能是 CType 基类实例
|
||
# (而非具体子类如 CInt),导致 _resolve_typedef_int_width 无法解析位宽。
|
||
# 此时从 Node.value 提取类型名,设置 OriginalType 为字符串,
|
||
# 让 _resolve_typedef_int_width 通过 CTypeRegistry.GetClassByName 解析。
|
||
if ValueTypeInfo.BaseType and type(ValueTypeInfo.BaseType) is t.CType:
|
||
if isinstance(Node.value, ast.Attribute) and getattr(Node.value.value, 'id', None) == 't':
|
||
type_name: str = Node.value.attr
|
||
resolved_cls = CTypeRegistry.GetClassByName(type_name)
|
||
if resolved_cls is not None:
|
||
ValueTypeInfo.OriginalType = type_name
|
||
ValueTypeInfo.IsTypedef = True
|
||
ValueTypeInfo.Name = VarName
|
||
# 设置 OriginalType,确保 _resolve_name 能解析(MergeTypes 不为 BinOp 结果设置 OriginalType)
|
||
if not ValueTypeInfo.OriginalType:
|
||
OriginalCopy: CTypeInfo = ValueTypeInfo.Copy()
|
||
OriginalCopy.IsTypedef = False
|
||
OriginalCopy.Name = ''
|
||
ValueTypeInfo.OriginalType = OriginalCopy
|
||
self.Trans.SymbolTable.insert(VarName, ValueTypeInfo)
|
||
if module_name:
|
||
FullName = f"{module_name}.{VarName}"
|
||
self.Trans.SymbolTable.insert(FullName, ValueTypeInfo)
|
||
return
|
||
TTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation)
|
||
if TTypeInfo:
|
||
TTypeInfo.IsTypedef = True
|
||
TTypeInfo.Name = VarName
|
||
self.Trans.SymbolTable.insert(VarName, TTypeInfo)
|
||
if module_name:
|
||
FullName = f"{module_name}.{VarName}"
|
||
self.Trans.SymbolTable.insert(FullName, TTypeInfo)
|
||
return
|
||
|
||
if IsCDefine:
|
||
# CDefine 宏不生成 LLVM 全局变量
|
||
# 治本修复(方案C):裸名只注册到本模块 _define_constants,
|
||
# 不再注册到跨模块共享的 SymbolTable,避免同名 CDefine 污染其他模块的全局变量
|
||
# (如 Phase1.SHA1_LEN=17 覆盖 VpiReader.SHA1_LEN=16 导致双点链接错误)。
|
||
# 跨模块导入通过 _RegisterFromImportAliases 查带前缀全名后注册到本模块 _define_constants。
|
||
DefineValue = None
|
||
if Node.value:
|
||
DefineValue = self._ExtractConstValue(Node.value)
|
||
# 仅本模块 CDefine 注册到 _define_constants(供 _HandleNameLlvm 步骤1 查找)
|
||
if module_name is None and DefineValue is not None:
|
||
define_constants = vars(Gen).setdefault('_define_constants', {})
|
||
if VarName not in define_constants:
|
||
define_constants[VarName] = DefineValue
|
||
# 如果提供了模块名,注册带模块前缀的全名到 SymbolTable
|
||
# (供 _RegisterFromImportAliases 的 from X import Y 显式导入查找)
|
||
if module_name:
|
||
FullName = f"{module_name}.{VarName}"
|
||
existing_full = self.Trans.SymbolTable.lookup(FullName)
|
||
if not (existing_full and existing_full.IsTypedef and existing_full.OriginalType):
|
||
self._RegisterCDefineSymbol(FullName, DefineValue, Node.lineno, ModulePath or 'unknown')
|
||
return # 不再继续生成 LLVM 全局变量
|
||
|
||
# t.State 标记的全局变量:只声明不定义(external linkage),避免 DSO/non-DSO 重定义
|
||
if TypeInfo and TypeInfo.IsState:
|
||
if VarName not in Gen.module.globals:
|
||
gv = ir.GlobalVariable(Gen.module, var_type, name=VarName)
|
||
gv.linkage = 'external'
|
||
Gen._export_funcs.add(VarName)
|
||
return
|
||
|
||
gv = ir.GlobalVariable(Gen.module, var_type, name=VarName)
|
||
# 外部全局变量声明:只声明不定义(external linkage),避免多重定义
|
||
gv.linkage = 'external'
|
||
elif isinstance(Node, ast.Assign) and Node.targets and isinstance(Node.targets[0], ast.Name):
|
||
VarName = Node.targets[0].id
|
||
if VarName.startswith('_'):
|
||
return
|
||
if VarName in Gen.module.globals:
|
||
return
|
||
gv = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName)
|
||
gv.linkage = 'external'
|
||
|
||
def _ExtractValue(self, value_node: ast.AST, target_type: ir.Type, var_name: str | None = None) -> ir.Constant | None:
|
||
"""Extract constant value from AST node for global variable initializer"""
|
||
# 处理负号
|
||
if isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.USub):
|
||
if isinstance(value_node.operand, ast.Constant) and isinstance(value_node.operand.value, int):
|
||
neg_val = -value_node.operand.value
|
||
if isinstance(target_type, ir.IntType):
|
||
return ir.Constant(target_type, neg_val)
|
||
return ir.Constant(ir.IntType(32), neg_val)
|
||
# 处理正号
|
||
elif isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.UAdd):
|
||
return self._ExtractValue(value_node.operand, target_type)
|
||
|
||
if isinstance(value_node, ast.Constant):
|
||
if isinstance(value_node.value, bool):
|
||
if isinstance(target_type, ir.IntType):
|
||
return ir.Constant(target_type, 1 if value_node.value else 0)
|
||
return ir.Constant(ir.IntType(32), 1 if value_node.value else 0)
|
||
elif isinstance(value_node.value, int):
|
||
if isinstance(target_type, ir.IntType):
|
||
return ir.Constant(target_type, value_node.value)
|
||
return ir.Constant(ir.IntType(32), value_node.value)
|
||
elif isinstance(value_node.value, str):
|
||
str_val = value_node.value + '\x00'
|
||
str_bytes = str_val.encode('utf-8')
|
||
arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes))
|
||
Gen = self.Trans.LlvmGen
|
||
gv_name = var_name + '_str' if var_name else f"str_global_{id(value_node)}"
|
||
if gv_name in Gen.module.globals:
|
||
gv = Gen.module.globals[gv_name]
|
||
else:
|
||
gv = ir.GlobalVariable(Gen.module, arr_type, name=gv_name)
|
||
gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
|
||
gv.linkage = 'internal'
|
||
ptr_type = ir.PointerType(ir.IntType(8))
|
||
return ir.Constant(ptr_type, gv.reference)
|
||
elif isinstance(value_node.value, bool):
|
||
return ir.Constant(ir.IntType(32), 1 if value_node.value else 0)
|
||
elif value_node.value is None:
|
||
if isinstance(target_type, ir.BaseStructType):
|
||
return ir.Constant(target_type, None)
|
||
if isinstance(target_type, ir.PointerType):
|
||
return ir.Constant(target_type, None)
|
||
return ir.Constant(target_type, 0)
|
||
elif isinstance(value_node, ast.Name):
|
||
if value_node.id == 'None':
|
||
if isinstance(target_type, ir.BaseStructType):
|
||
return ir.Constant(target_type, None)
|
||
if isinstance(target_type, ir.PointerType):
|
||
return ir.Constant(target_type, None)
|
||
return ir.Constant(target_type, 0)
|
||
return None
|
||
|
||
def _ExtractConstValue(self, value_node: ast.AST) -> object:
|
||
"""Extract constant value from AST node for SymbolTable registration
|
||
支持常量、一元运算符、二元运算符和常量引用
|
||
"""
|
||
if isinstance(value_node, ast.Constant):
|
||
return value_node.value
|
||
elif isinstance(value_node, ast.BinOp):
|
||
left = self._ExtractConstValue(value_node.left)
|
||
right = self._ExtractConstValue(value_node.right)
|
||
if left is None or right is None:
|
||
return None
|
||
if isinstance(value_node.op, ast.Add):
|
||
return left + right
|
||
elif isinstance(value_node.op, ast.Sub):
|
||
return left - right
|
||
elif isinstance(value_node.op, ast.Mult):
|
||
return left * right
|
||
elif isinstance(value_node.op, ast.Div):
|
||
return left // right if isinstance(left, int) and isinstance(right, int) else left / right
|
||
elif isinstance(value_node.op, ast.FloorDiv):
|
||
return left // right
|
||
elif isinstance(value_node.op, ast.Mod):
|
||
return left % right
|
||
elif isinstance(value_node.op, ast.Pow):
|
||
return left ** right
|
||
elif isinstance(value_node.op, ast.LShift):
|
||
return left << right
|
||
elif isinstance(value_node.op, ast.RShift):
|
||
return left >> right
|
||
elif isinstance(value_node.op, ast.BitOr):
|
||
return left | right
|
||
elif isinstance(value_node.op, ast.BitXor):
|
||
return left ^ right
|
||
elif isinstance(value_node.op, ast.BitAnd):
|
||
return left & right
|
||
elif isinstance(value_node, ast.UnaryOp):
|
||
operand = self._ExtractConstValue(value_node.operand)
|
||
if operand is None:
|
||
return None
|
||
if isinstance(value_node.op, ast.USub):
|
||
return -operand
|
||
elif isinstance(value_node.op, ast.UAdd):
|
||
return +operand
|
||
elif isinstance(value_node.op, ast.Invert):
|
||
return ~operand
|
||
elif isinstance(value_node, ast.Name):
|
||
if value_node.id in getattr(self.Trans, 'SymbolTable', {}):
|
||
info = self.Trans.SymbolTable[value_node.id]
|
||
if info.IsDefine and info.DefineValue is not None:
|
||
return info.DefineValue
|
||
elif isinstance(value_node, ast.Call):
|
||
if isinstance(value_node.func, ast.Attribute):
|
||
if getattr(value_node.func.value, 'id', None) == 't':
|
||
if value_node.args:
|
||
return self._ExtractConstValue(value_node.args[0])
|
||
elif isinstance(value_node.func, ast.Name):
|
||
if value_node.args:
|
||
return self._ExtractConstValue(value_node.args[0])
|
||
return None
|
||
|
||
def _ExtractTypedefOriginalType(self, value_node: ast.AST) -> str | None:
|
||
if isinstance(value_node, ast.Attribute):
|
||
if getattr(value_node.value, 'id', None) == 't':
|
||
llvm_str = CTypeRegistry.NameToLLVM(value_node.attr)
|
||
if llvm_str:
|
||
return llvm_str
|
||
elif isinstance(value_node, ast.Name):
|
||
llvm_str = CTypeRegistry.NameToLLVM(value_node.id)
|
||
if llvm_str:
|
||
return llvm_str
|
||
elif isinstance(value_node, ast.BinOp) and isinstance(value_node.op, ast.BitOr):
|
||
left = self._ExtractTypedefOriginalType(value_node.left)
|
||
right = self._ExtractTypedefOriginalType(value_node.right)
|
||
parts = []
|
||
if left:
|
||
parts.append(left)
|
||
if right:
|
||
parts.append(right)
|
||
if parts:
|
||
return '|'.join(parts)
|
||
elif isinstance(value_node, ast.Call):
|
||
if isinstance(value_node.func, ast.Attribute):
|
||
if getattr(value_node.func.value, 'id', None) == 't':
|
||
llvm_str = CTypeRegistry.NameToLLVM(value_node.func.attr)
|
||
if llvm_str:
|
||
return llvm_str
|
||
elif isinstance(value_node.func, ast.Name):
|
||
llvm_str = CTypeRegistry.NameToLLVM(value_node.func.id)
|
||
if llvm_str:
|
||
return llvm_str
|
||
return None
|
||
|
||
def _RegisterCDefineSymbol(self, FullName: str, DefineValue: object, lineno: int, FilePath: str) -> None:
|
||
"""Register CDefine constant to SymbolTable"""
|
||
# 防御:新值为 None 但已存在非 None 值时保留旧值。
|
||
# 翻译期间声明发射调用 _ExtractConstValue(不支持 ast.Attribute 形式如 t.CSizeT().Size),
|
||
# 会返回 None 并覆盖共享符号表中已由 ConstEvaluator 正确求值的值(如 8)。
|
||
# 共享符号表在 _build_shared_symbol_data 阶段已正确求值,此处不应覆盖。
|
||
if DefineValue is None:
|
||
existing: Any = self.Trans.SymbolTable.lookup(FullName)
|
||
if existing and existing.IsDefine and existing.DefineValue is not None:
|
||
return
|
||
info = CTypeInfo()
|
||
info.IsDefine = True
|
||
info.DefineValue = DefineValue
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
# 直接添加到 SymbolTable 字典
|
||
self.Trans.SymbolTable.insert(FullName, info)
|
||
|
||
def _check_annotation_for_state(self, annotation: ast.AST) -> bool:
|
||
return (AnnotationContainsName(annotation, 'CExport') or
|
||
AnnotationContainsName(annotation, 'CExtern') or
|
||
AnnotationContainsName(annotation, 'State'))
|
||
|
||
def _check_decorators_for_state(self, Node: ast.FunctionDef) -> bool:
|
||
"""检查装饰器列表中是否包含 @t.CExport / @t.CExtern / @t.State(t.CExport 既能作返回值注解也能作装饰器)"""
|
||
if not Node.decorator_list:
|
||
return False
|
||
for decorator in Node.decorator_list:
|
||
if (AnnotationContainsName(decorator, 'CExport') or
|
||
AnnotationContainsName(decorator, 'CExtern') or
|
||
AnnotationContainsName(decorator, 'State')):
|
||
return True
|
||
return False
|
||
|
||
def _EmitExternalFuncDeclLlvm(self, Node: ast.FunctionDef, Gen: LlvmGeneratorMixin, is_class_method: bool = False, source_module_name: str | None = None, register_module_name: str | None = None, reexport_module_names: list[str] | None = None) -> None:
|
||
FuncName = Node.name
|
||
if (Node.returns and self._check_annotation_for_state(Node.returns)) or self._check_decorators_for_state(Node):
|
||
Gen._export_funcs.add(FuncName) # t.State 标记的外部 C 函数必须保持原始名称,以便链接器解析
|
||
if source_module_name and source_module_name not in ('c', 't'):
|
||
_src_sha1 = Gen.ModuleSha1Map.get(source_module_name)
|
||
if not _src_sha1:
|
||
_init_name = f"{source_module_name}.__init__"
|
||
if _init_name in Gen.ModuleSha1Map:
|
||
_src_sha1 = Gen.ModuleSha1Map[_init_name]
|
||
if _src_sha1:
|
||
Gen._export_extern_funcs[FuncName] = _src_sha1
|
||
if FuncName in Gen.functions:
|
||
return
|
||
if Node.decorator_list:
|
||
for decorator in Node.decorator_list:
|
||
if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute):
|
||
# fail-fast:`@c.CReturn` 已移除,扫到即报错。
|
||
if decorator.func.attr == 'CReturn':
|
||
raise SyntaxError(
|
||
f"函数 '{FuncName}':`@c.CReturn` 已移除:请改用返回注解 "
|
||
f"`-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)"
|
||
)
|
||
IsPtr = False
|
||
ReturnTypeInfo = None
|
||
if Node.returns:
|
||
try:
|
||
ReturnTypeInfo = self.ResolveAnnotationType(Node.returns)
|
||
# 仅指针层数 <=1 才算裸 str/bytes(i8*)。
|
||
# `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。
|
||
if ReturnTypeInfo and ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1:
|
||
ReturnTypeInfo = CTypeInfo()
|
||
ReturnTypeInfo.BaseType = t.CChar()
|
||
ReturnTypeInfo.PtrCount = 1
|
||
if ReturnTypeInfo is None:
|
||
ReturnTypeInfo = CTypeInfo()
|
||
ReturnTypeInfo.BaseType = t.CInt()
|
||
IsPtr = ReturnTypeInfo.IsPtr
|
||
except RecursionError:
|
||
# 家族口径(HandlesBase.py:1060-1068):RecursionError 是类型解析链成环的
|
||
# 真缺陷信号,不允许退化成"默认 CInt"(那会让被指类丢失、下游全部错型)。
|
||
raise
|
||
except Exception as e: # 回退:返回类型解析失败时使用默认 CInt
|
||
# NoneType 异常(如对 None 调 endswith)是数据缺失的正常兜底场景,
|
||
# 静默不输出 WARN(干扰太多);其他异常保留提示。
|
||
if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)):
|
||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
||
ReturnTypeInfo = CTypeInfo()
|
||
ReturnTypeInfo.BaseType = t.CInt()
|
||
else:
|
||
ReturnTypeInfo = CTypeInfo()
|
||
ReturnTypeInfo.BaseType = t.CInt()
|
||
ReturnType = Gen._ctype_to_llvm(ReturnTypeInfo)
|
||
if (isinstance(ReturnType, ir.VoidType) or (isinstance(ReturnType, ir.PointerType) and isinstance(ReturnType.pointee, ir.VoidType))) and Node.returns:
|
||
RetAnnName = None
|
||
if isinstance(Node.returns, ast.Name):
|
||
RetAnnName = Node.returns.id
|
||
elif isinstance(Node.returns, ast.Attribute):
|
||
RetAnnName = Node.returns.attr
|
||
elif isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr):
|
||
left = Node.returns.left
|
||
if isinstance(left, ast.Name):
|
||
RetAnnName = left.id
|
||
elif isinstance(left, ast.Attribute):
|
||
RetAnnName = left.attr
|
||
if RetAnnName and RetAnnName in Gen.structs:
|
||
ReturnType = ir.PointerType(Gen.structs[RetAnnName])
|
||
elif IsPtr or RetAnnName:
|
||
ReturnType = ir.PointerType(ir.IntType(8))
|
||
# __new__ 方法应返回类指针类型(堆分配替换),与 _GetFunctionSignatureLlvm/_EmitFunctionLlvm 保持一致
|
||
# .pyi 生成器对无返回类型注解的 __new__ 默认使用 t.CInt,这里修正为 ClassName*
|
||
if '.__new__' in FuncName:
|
||
_np = FuncName.split('.')
|
||
if len(_np[0]) == 16 and all(_c in '0123456789abcdef' for _c in _np[0]):
|
||
_new_class_name = _np[1]
|
||
else:
|
||
_new_class_name = _np[0]
|
||
# 治本修复:优先用 source sha1 查找正确的 struct,避免同名类冲突时返回错误类型
|
||
_new_struct = None
|
||
_new_src_sha1: str | None = None
|
||
if source_module_name and hasattr(Gen, 'ModuleSha1Map'):
|
||
_new_src_sha1 = Gen.ModuleSha1Map.get(source_module_name)
|
||
if not _new_src_sha1:
|
||
_init_name = f"{source_module_name}.__init__"
|
||
if _init_name in Gen.ModuleSha1Map:
|
||
_new_src_sha1 = Gen.ModuleSha1Map[_init_name]
|
||
if _new_src_sha1 and _new_class_name:
|
||
_new_struct = Gen.structs.get(f"{_new_src_sha1}.{_new_class_name}")
|
||
if _new_struct is None:
|
||
_new_struct = Gen.structs.get(_new_class_name)
|
||
if _new_struct is None:
|
||
_full_name = FuncName.split('.__new__')[0]
|
||
_new_struct = Gen.structs.get(_full_name)
|
||
if _new_struct:
|
||
if isinstance(_new_struct, ir.PointerType):
|
||
ReturnType = _new_struct
|
||
else:
|
||
ReturnType = ir.PointerType(_new_struct)
|
||
ParamTypes = []
|
||
is_method = is_class_method or '.__' in FuncName
|
||
class_name_for_method = FuncName.split('.')[0] if '.' in FuncName else None
|
||
class_is_cpython = class_name_for_method and self.Trans.SymbolTable.is_struct(class_name_for_method)
|
||
# 治本修复:通过 source_module_name 获取源模块 sha1,查找正确的 struct。
|
||
# 当存在同名类冲突(如 stmts.py:Module 和 __module.py:Module)时,
|
||
# Gen.structs[short_name] 可能指向错误模块的 struct(set_body 已锁定错误布局)。
|
||
# 优先查找以 sha1 为前缀的完整 key,避免使用错误的 struct 作为 self 参数类型。
|
||
_self_struct_sha1: str | None = None
|
||
if source_module_name and hasattr(Gen, 'ModuleSha1Map'):
|
||
_self_struct_sha1 = Gen.ModuleSha1Map.get(source_module_name)
|
||
if not _self_struct_sha1:
|
||
_init_name = f"{source_module_name}.__init__"
|
||
if _init_name in Gen.ModuleSha1Map:
|
||
_self_struct_sha1 = Gen.ModuleSha1Map[_init_name]
|
||
for i, Arg in enumerate(Node.args.args):
|
||
if i == 0 and is_method:
|
||
# self parameter of a method should always be a pointer to the struct
|
||
_self_struct = None
|
||
if _self_struct_sha1 and class_name_for_method:
|
||
_full_key = f"{_self_struct_sha1}.{class_name_for_method}"
|
||
_self_struct = Gen.structs.get(_full_key)
|
||
if _self_struct is None:
|
||
_self_struct = Gen.structs.get(class_name_for_method or FuncName.split('.')[0], ir.IntType(8))
|
||
ParamType = ir.PointerType(_self_struct)
|
||
elif Arg.annotation:
|
||
try:
|
||
ParamTypeInfo = self.ResolveAnnotationType(Arg.annotation)
|
||
if ParamTypeInfo is None:
|
||
ParamTypeInfo = CTypeInfo()
|
||
ParamTypeInfo.BaseType = t.CInt()
|
||
ParamIsPtr = ParamTypeInfo.IsPtr
|
||
if i == 0 and is_method and Arg.arg == 'self' and class_is_cpython:
|
||
ParamIsPtr = True
|
||
ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1)
|
||
# 仅当指针层数 <=1 时才压平为 i8*(裸 str/bytes)。
|
||
# `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),
|
||
# 不能压平,否则形参类型错误导致 _adjust_args 多 load 一层崩溃。
|
||
if (ParamTypeInfo.IsStr and ParamTypeInfo.PtrCount <= 1) or (isinstance(Arg.annotation, ast.Name) and Arg.annotation.id in ('str', 'bytes')):
|
||
ParamTypeInfo = CTypeInfo()
|
||
ParamTypeInfo.BaseType = t.CChar()
|
||
ParamTypeInfo.PtrCount = 1
|
||
ParamIsPtr = True
|
||
# C 语言中数组参数退化为指针:list[type, N] → type*
|
||
if ParamTypeInfo.ArrayDims:
|
||
ParamTypeInfo.ArrayDims = []
|
||
ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1)
|
||
ParamType = Gen._ctype_to_llvm(ParamTypeInfo)
|
||
except RecursionError:
|
||
# 家族口径(HandlesBase.py:1060-1068):RecursionError 是类型解析链成环的
|
||
# 真缺陷信号,不允许退化成"默认 i32"(形参错型会让 _adjust_args 行为错乱)。
|
||
raise
|
||
except Exception as e: # 回退:参数类型解析失败时使用默认 i32
|
||
# NoneType 异常是数据缺失的正常兜底,静默不 WARN(干扰太多)
|
||
if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)):
|
||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
||
ParamType = ir.IntType(32)
|
||
if isinstance(ParamType, ir.VoidType) or (isinstance(ParamType, ir.PointerType) and isinstance(ParamType.pointee, ir.VoidType)):
|
||
AnnName = None
|
||
if isinstance(Arg.annotation, ast.Name):
|
||
AnnName = Arg.annotation.id
|
||
elif isinstance(Arg.annotation, ast.Attribute):
|
||
AnnName = Arg.annotation.attr
|
||
elif isinstance(Arg.annotation, ast.BinOp) and isinstance(Arg.annotation.op, ast.BitOr):
|
||
AnnName = ExtractTypeNameFromBinOp(Arg.annotation)
|
||
if AnnName and AnnName in Gen.structs:
|
||
ParamType = ir.PointerType(Gen.structs[AnnName])
|
||
else:
|
||
ParamType = ir.PointerType(ir.IntType(8))
|
||
else:
|
||
ParamType = ir.IntType(32)
|
||
ParamTypes.append(ParamType)
|
||
IsVariadic = Node.args.vararg is not None
|
||
FuncType = ir.FunctionType(ReturnType, ParamTypes, var_arg=IsVariadic)
|
||
_is_export = False
|
||
if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.IsState:
|
||
_is_export = True
|
||
if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.Storage and isinstance(ReturnTypeInfo.Storage, t.CExport) and not ReturnTypeInfo.IsState:
|
||
Gen._export_funcs.add(FuncName)
|
||
_is_export = True
|
||
if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.Storage and isinstance(ReturnTypeInfo.Storage, t.CExtern):
|
||
Gen._export_funcs.add(FuncName)
|
||
_is_export = True
|
||
if not _is_export:
|
||
if Node.returns and self._check_annotation_for_state(Node.returns):
|
||
_is_export = True
|
||
elif self._check_decorators_for_state(Node):
|
||
_is_export = True
|
||
MangledName = Gen._mangle_func_name(FuncName, module_name=source_module_name)
|
||
func = Gen._get_or_declare_function(MangledName, FuncType)
|
||
Gen.functions[MangledName] = func
|
||
Gen.functions[FuncName] = func
|
||
func_meta = FuncMeta.NONE
|
||
if Node.decorator_list:
|
||
for d in Node.decorator_list:
|
||
if isinstance(d, ast.Name):
|
||
if d.id == 'staticmethod':
|
||
func_meta |= FuncMeta.STATIC_METHOD
|
||
elif d.id == 'property':
|
||
func_meta |= FuncMeta.PROPERTY_GETTER
|
||
elif d.id == 'classmethod':
|
||
func_meta |= FuncMeta.CLASS_METHOD
|
||
elif isinstance(d, ast.Attribute):
|
||
if isinstance(d.value, ast.Name):
|
||
# 支持 @property.setter 和 @propname.setter 两种形式
|
||
if d.value.id == 'property' or d.attr in ('setter', 'getter', 'deleter'):
|
||
if d.attr == 'setter':
|
||
func_meta |= FuncMeta.PROPERTY_SETTER
|
||
elif d.attr == 'getter':
|
||
func_meta |= FuncMeta.PROPERTY_GETTER
|
||
elif d.attr == 'deleter':
|
||
func_meta |= FuncMeta.PROPERTY_DELETER
|
||
if not self.Trans.SymbolTable.has(FuncName):
|
||
FuncInfo = CTypeInfo()
|
||
FuncInfo.Name = FuncName
|
||
FuncInfo.IsFunction = True
|
||
FuncInfo.MetaList = func_meta
|
||
self.Trans.SymbolTable.insert(FuncName, FuncInfo)
|
||
else:
|
||
existing = self.Trans.SymbolTable[FuncName]
|
||
if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE:
|
||
existing.MetaList = func_meta
|
||
# 同时注册带模块前缀的符号,以便 module.func_name 查找能命中
|
||
if source_module_name and source_module_name not in ('c', 't'):
|
||
FullSymKey = f"{source_module_name}.{FuncName}"
|
||
if not self.Trans.SymbolTable.has(FullSymKey):
|
||
FullFuncInfo = CTypeInfo()
|
||
FullFuncInfo.Name = FullSymKey
|
||
FullFuncInfo.IsFunction = True
|
||
FullFuncInfo.MetaList = func_meta
|
||
self.Trans.SymbolTable.insert(FullSymKey, FullFuncInfo)
|
||
# property setter/deleter: 在原始 PropKey(不带后缀)下注册 MetaList
|
||
if FuncMeta.PROPERTY_SETTER in func_meta or FuncMeta.PROPERTY_DELETER in func_meta:
|
||
BasePropKey = FuncName.replace('$set', '').replace('$del', '')
|
||
base_existing = self.Trans.SymbolTable.lookup(BasePropKey)
|
||
if base_existing:
|
||
if func_meta != FuncMeta.NONE:
|
||
base_existing.MetaList = base_existing.MetaList | func_meta
|
||
else:
|
||
PropInfo = CTypeInfo()
|
||
PropInfo.Name = BasePropKey
|
||
PropInfo.IsFunction = True
|
||
PropInfo.MetaList = func_meta
|
||
self.Trans.SymbolTable.insert(BasePropKey, PropInfo)
|
||
if register_module_name and register_module_name != source_module_name:
|
||
ReexportName = Gen._mangle_func_name(FuncName, module_name=register_module_name)
|
||
Gen.functions[ReexportName] = func
|
||
if reexport_module_names:
|
||
for reexport_mod in reexport_module_names:
|
||
if reexport_mod and reexport_mod != source_module_name:
|
||
ReexportName2 = Gen._mangle_func_name(FuncName, module_name=reexport_mod)
|
||
if ReexportName2 not in Gen.functions:
|
||
Gen.functions[ReexportName2] = func
|
||
|
||
def _ClassifyExternalClass(self, Node: ast.ClassDef) -> tuple:
|
||
"""遍历 Node.bases/decorator_list 做类别判断(纯函数、与 Gen 无关)。
|
||
|
||
结果由 _EmitExternalClassDeclLlvm 按 (source_sha1, class_name) 类级缓存,
|
||
同一 worker 进程内多个任务对同一跨模块类重复调用时跳过本遍历。
|
||
返回 (IsGenericClass, IsCenum, IsRenum, IsCpythonObject, IsCVTable,
|
||
IsNoVTable, IsPacked)。
|
||
|
||
PEP 695 泛型类(如 GSListNode[T])特殊:跨模块继承展平需要从泛型基类
|
||
获取字段(如 Next),否则子类(如 Value(GSListNode[Value]))的
|
||
class_members 会缺少继承字段;但泛型类的方法声明必须跳过(方法参数
|
||
类型 T 会被解析为 opaque struct,LLVM 不允许 opaque struct 作为值参数)。
|
||
"""
|
||
IsGenericClass: bool = hasattr(Node, 'type_params') and bool(Node.type_params)
|
||
IsCenum = False
|
||
IsRenum = False
|
||
IsCpythonObject = False
|
||
IsCVTable = False
|
||
IsNoVTable = False
|
||
if Node.bases:
|
||
for base in Node.bases:
|
||
if getattr(base, 'attr', None):
|
||
if base.attr == 'REnum':
|
||
IsRenum = True
|
||
break
|
||
elif base.attr == 'CEnum' or base.attr == 'Enum':
|
||
IsCenum = True
|
||
break
|
||
elif getattr(base, 'id', None):
|
||
if base.id == 'REnum':
|
||
IsRenum = True
|
||
break
|
||
elif base.id == 'CEnum' or base.id == 'Enum':
|
||
IsCenum = True
|
||
break
|
||
if not IsCenum and not IsRenum and getattr(Node, 'decorator_list', None):
|
||
for decorator in Node.decorator_list:
|
||
if isinstance(decorator, ast.Attribute):
|
||
if getattr(decorator.value, 'id', None) == 't':
|
||
if decorator.attr == 'Object':
|
||
IsCpythonObject = True
|
||
elif decorator.attr == 'CVTable':
|
||
IsCVTable = True
|
||
elif decorator.attr == 'NoVTable':
|
||
IsNoVTable = True
|
||
elif decorator.attr == 'REnum':
|
||
IsRenum = True
|
||
elif decorator.attr == 'CEnum' or decorator.attr == 'Enum':
|
||
IsCenum = True
|
||
elif isinstance(decorator, ast.Call):
|
||
if isinstance(decorator.func, ast.Attribute):
|
||
if getattr(decorator.func.value, 'id', None) == 't':
|
||
if decorator.func.attr == 'Object':
|
||
IsCpythonObject = True
|
||
elif decorator.func.attr == 'CVTable':
|
||
IsCVTable = True
|
||
elif decorator.func.attr == 'NoVTable':
|
||
IsNoVTable = True
|
||
elif isinstance(decorator.func, ast.Name):
|
||
if decorator.func.id == 'Object':
|
||
IsCpythonObject = True
|
||
elif decorator.func.id == 'CVTable':
|
||
IsCVTable = True
|
||
elif decorator.func.id == 'NoVTable':
|
||
IsNoVTable = True
|
||
IsPacked = False
|
||
if getattr(Node, 'decorator_list', None):
|
||
for decorator in Node.decorator_list:
|
||
if isinstance(decorator, ast.Call):
|
||
if isinstance(decorator.func, ast.Attribute):
|
||
if getattr(decorator.func.value, 'id', None) == 'c' and decorator.func.attr == 'Attribute':
|
||
for arg in decorator.args:
|
||
if isinstance(arg, ast.Attribute):
|
||
if isinstance(arg.value, ast.Attribute):
|
||
if getattr(arg.value.value, 'id', None) == 't' and arg.value.attr == 'attr' and arg.attr == 'packed':
|
||
IsPacked = True
|
||
# 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable:
|
||
# 与 HandlesClassDef._resolve_class_flags / TPV _detect_vtable_status 的
|
||
# `_has_any_func_cvtable` 分支对齐(跨模块这里只能判"类级 NoVTable /
|
||
# 无真实父类"两种可判定形态;父类 vtable 状态由 .pyi 按需注册路径解析)。
|
||
if not IsCVTable and ClassHasFuncLevelCVTable(Node):
|
||
if IsNoVTable or not _HasRealParentClass(Node):
|
||
IsCVTable = True
|
||
return (IsGenericClass, IsCenum, IsRenum, IsCpythonObject,
|
||
IsCVTable, IsNoVTable, IsPacked)
|
||
|
||
def _EmitExternalClassDeclLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin, module_name: str | None = None, actual_module_name: str | None = None, source_sha1: str | None = None) -> None:
|
||
ClassName = Node.name
|
||
# Compute source_sha1 up front (moved from mid-function) so it can serve as
|
||
# the key of the class-level classification cache. Prefer the explicit
|
||
# source_sha1 passed by _TryLoadClassMembersFromPyi (module_name lookup may
|
||
# fail when the current module does not import the defining module directly).
|
||
if not source_sha1:
|
||
if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map:
|
||
source_sha1 = Gen.ModuleSha1Map[actual_module_name]
|
||
elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map:
|
||
source_sha1 = Gen.ModuleSha1Map[module_name]
|
||
# Class-level classification cache: the bases/decorator_list walk below is pure
|
||
# Gen-independent computation. Repeated calls on the same (source_sha1, class_name)
|
||
# across worker tasks (same process) skip the walk. When source_sha1 is None
|
||
# (lookup failed) neither read nor write the cache, to avoid mismatching
|
||
# same-named classes from different modules under a shared None key.
|
||
_classify_key = (source_sha1, ClassName) if source_sha1 else None
|
||
_cached_classify = self._extern_class_classify_cache.get(_classify_key) if _classify_key else None
|
||
if _cached_classify is None:
|
||
_cached_classify = self._ClassifyExternalClass(Node)
|
||
if _classify_key is not None:
|
||
self._extern_class_classify_cache[_classify_key] = _cached_classify
|
||
(IsGenericClass, IsCenum, IsRenum, IsCpythonObject,
|
||
IsCVTable, IsNoVTable, IsPacked) = _cached_classify
|
||
if ClassName.startswith('__') and not IsCpythonObject and not IsCenum and not IsRenum:
|
||
return
|
||
if IsPacked:
|
||
Gen.class_packed.add(ClassName)
|
||
if IsCpythonObject:
|
||
CInfo = CTypeInfo()
|
||
CInfo.Name = ClassName
|
||
CInfo.IsCpythonObject = True
|
||
CInfo.IsStruct = True
|
||
if module_name:
|
||
FullName = f"{module_name}.{ClassName}"
|
||
self.Trans.SymbolTable.insert(FullName, CInfo)
|
||
self.Trans.SymbolTable.insert(ClassName, CInfo)
|
||
if IsCenum:
|
||
EnumTypeNode = CTypeInfo()
|
||
EnumTypeNode.Name = ClassName
|
||
EnumTypeNode.BaseType = t.CEnum(ClassName)
|
||
EnumTypeNode.IsEnum = True
|
||
# [跨模块枚举宽度] 读侧重推导:.vpi/.pyi 保留成员注解(`Name: T`),
|
||
# 故在本处按"最宽成员类型"重算(与定义端 HandlesClassDef 同一规则源
|
||
# EnumClassIntWidth),使跨模块使用同一枚举时宽度一致。本函数会以短名
|
||
# 覆盖共享符号表(_shared_sym.json pickle)中的同名条目,若不在此登记
|
||
# 宽度,覆盖后即退化为默认 i32(= 上一轮遗留缺口)。旧产物缺注解时
|
||
# 自然回落 32(Default),不崩。
|
||
EnumWidth: int = EnumClassIntWidth(Node.body, self.Trans.SymbolTable)
|
||
EnumTypeNode.set('enum_width', EnumWidth)
|
||
self.Trans.SymbolTable.insert(ClassName, EnumTypeNode)
|
||
next_enum_value: int = 0
|
||
for item in Node.body:
|
||
VarName = None
|
||
ItemValue = None
|
||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
VarName = item.target.id
|
||
ItemValue = item.value
|
||
elif isinstance(item, ast.Assign):
|
||
for target in item.targets:
|
||
if isinstance(target, ast.Name):
|
||
VarName = target.id
|
||
break
|
||
ItemValue = item.value
|
||
if VarName is None:
|
||
continue
|
||
# 提取枚举值:优先用显式赋值,否则自动编号
|
||
if ItemValue is not None and isinstance(ItemValue, ast.Constant) and isinstance(ItemValue.value, int):
|
||
value = ItemValue.value
|
||
next_enum_value = ItemValue.value + 1
|
||
else:
|
||
value = next_enum_value
|
||
next_enum_value += 1
|
||
MemberNode = CTypeInfo()
|
||
MemberNode.Name = VarName
|
||
MemberNode.BaseType = t.CEnum(ClassName)
|
||
MemberNode.value = value
|
||
MemberNode.EnumName = ClassName
|
||
MemberNode.Lineno = item.lineno
|
||
MemberNode.IsEnumMember = True
|
||
MemberNode.set('enum_width', EnumWidth)
|
||
self.Trans.SymbolTable.insert(VarName, MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode)
|
||
return
|
||
if IsRenum:
|
||
# REnum 跨模块导入:注册 REnum 类型 + 变体成员到 SymbolTable
|
||
# 对齐同模块 _EmitREnumLlvm 的注册逻辑
|
||
RenumTypeNode = CTypeInfo()
|
||
RenumTypeNode.Name = ClassName
|
||
RenumTypeNode.IsRenum = True
|
||
RenumTypeNode.IsEnum = True # 兼容 IsEnum 检查
|
||
# REnum 在内存布局上是 struct,必须设置 IsStruct=True 才能让
|
||
# HandlesTypeMerge 的 CPtr 吸收逻辑生效(REnumType | t.CPtr = REnumType*)
|
||
RenumTypeNode.IsStruct = True
|
||
if module_name:
|
||
FullName = f"{module_name}.{ClassName}"
|
||
self.Trans.SymbolTable.insert(FullName, RenumTypeNode)
|
||
self.Trans.SymbolTable.insert(ClassName, RenumTypeNode)
|
||
# 从 stub.ll 加载 REnum 结构体布局到 Gen.structs
|
||
self._TryLoadStructFromStub(ClassName, Gen)
|
||
# 记录 class_sha1_map(跨模块名修饰)
|
||
source_sha1 = None
|
||
if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map:
|
||
source_sha1 = Gen.ModuleSha1Map[actual_module_name]
|
||
elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map:
|
||
source_sha1 = Gen.ModuleSha1Map[module_name]
|
||
if source_sha1:
|
||
if not hasattr(Gen, 'class_sha1_map'):
|
||
Gen.class_sha1_map = {}
|
||
Gen.class_sha1_map[ClassName] = source_sha1
|
||
# 遍历嵌套 ClassDef 注册变体成员
|
||
# REnum body 是嵌套 ClassDef(变体定义),不是 AnnAssign/Assign
|
||
next_tag: int = 0
|
||
variant_names: list[str] = []
|
||
for item in Node.body:
|
||
if isinstance(item, ast.ClassDef):
|
||
VariantName = item.name
|
||
MemberNode = CTypeInfo()
|
||
MemberNode.Name = VariantName
|
||
MemberNode.value = next_tag
|
||
MemberNode.EnumName = ClassName
|
||
MemberNode.Lineno = item.lineno
|
||
MemberNode.IsEnumMember = True
|
||
self.Trans.SymbolTable.insert(VariantName, MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode)
|
||
variant_names.append(VariantName)
|
||
next_tag += 1
|
||
# 加载嵌套变体结构体(如 LLVMType_Int)到 Gen.structs,
|
||
# 并注册 class_members 以支持 _HandleREnumConstructLlvm 的 payload 存储和
|
||
# _HandleRenumMatchLlvm 的 binding 提取。
|
||
NestedStructName: str = f"{ClassName}_{VariantName}"
|
||
self._TryLoadStructFromStub(NestedStructName, Gen)
|
||
if NestedStructName not in Gen.class_members:
|
||
Gen.class_members[NestedStructName] = []
|
||
if NestedStructName not in Gen.class_member_signeds:
|
||
Gen.class_member_signeds[NestedStructName] = {}
|
||
if Gen.class_members[NestedStructName]:
|
||
# 已有成员(_TryLoadStructFromStub 可能已加载),跳过
|
||
pass
|
||
else:
|
||
Gen.class_members[NestedStructName].append(('__tag', ir.IntType(32)))
|
||
Gen.class_member_signeds[NestedStructName]['__tag'] = None
|
||
for nested_item in item.body:
|
||
# 只把 AnnAssign 当载荷声明;变体体里的 pass / docstring 等
|
||
# 非声明语句合法跳过(如 `class Void: pass`)。
|
||
if not isinstance(nested_item, ast.AnnAssign):
|
||
continue
|
||
_member_txt: str = ast.unparse(nested_item)
|
||
try:
|
||
parsed = self.CollectAnnAssignMember(nested_item, Gen)
|
||
except Exception as _pe:
|
||
# 历史上此处异常被上层吞掉 ⇒ 变体只剩 __tag ⇒ 变体构造静默
|
||
# 丢载荷、match 模式捕获绑不上(现场 LLVMType_Ptr / pointee_cs),
|
||
# 且循环中断使后续变体(Func/Array/...)整批缺失。改为响亮失败。
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
raise _FFE(
|
||
f"REnum 变体 '{ClassName}.{VariantName}' 的载荷成员 "
|
||
f"'{_member_txt}' 解析失败:{type(_pe).__name__}: {_pe} "
|
||
f"—— 拒绝留下只有 __tag 的 class_members"
|
||
f"(会导致变体构造丢载荷、match 捕获绑定缺失、后续变体整批缺失)"
|
||
) from _pe
|
||
if not parsed:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
raise _FFE(
|
||
f"REnum 变体 '{ClassName}.{VariantName}' 的载荷成员 "
|
||
f"'{_member_txt}' 未解析出类型(CollectAnnAssignMember 返回 None)"
|
||
f"—— 同上,拒绝静默降级为无载荷"
|
||
)
|
||
MName, MType, MTypeInfo = parsed
|
||
Gen.class_members[NestedStructName].append((MName, MType))
|
||
Gen.class_member_signeds[NestedStructName][MName] = None
|
||
# 当 stub 中没有嵌套变体结构体定义时(_TryLoadStructFromStub 失败),
|
||
# 使用主 REnum 结构体的 body (max_variant_struct) 创建嵌套变体结构体,
|
||
# 确保跨模块布局一致。否则 _generate_structs() Step 4 会从 class_members
|
||
# 设置最小布局 body,导致 GEP 偏移量与定义模块不匹配(offset 4 vs offset 8)。
|
||
NestedStructExisting = Gen.structs.get(NestedStructName)
|
||
NestedHasBody = (isinstance(NestedStructExisting, ir.IdentifiedStructType)
|
||
and NestedStructExisting.elements is not None
|
||
and len(NestedStructExisting.elements) > 0)
|
||
if not NestedHasBody:
|
||
MainRenumStruct = Gen.structs.get(ClassName)
|
||
if (isinstance(MainRenumStruct, ir.IdentifiedStructType)
|
||
and MainRenumStruct.elements is not None
|
||
and len(MainRenumStruct.elements) > 0):
|
||
NestedStruct = Gen._get_or_create_struct(NestedStructName, source_sha1=source_sha1)
|
||
if isinstance(NestedStruct, ir.IdentifiedStructType) and NestedStruct.is_opaque:
|
||
NestedStruct.set_body(*MainRenumStruct.elements)
|
||
elif isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
# 显式值变体(无 payload)
|
||
VariantName = item.target.id
|
||
value = next_tag
|
||
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||
value = item.value.value
|
||
MemberNode = CTypeInfo()
|
||
MemberNode.Name = VariantName
|
||
MemberNode.value = value
|
||
MemberNode.EnumName = ClassName
|
||
MemberNode.Lineno = item.lineno
|
||
MemberNode.IsEnumMember = True
|
||
self.Trans.SymbolTable.insert(VariantName, MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode)
|
||
variant_names.append(VariantName)
|
||
next_tag = value + 1
|
||
RenumTypeNode.RenumVariants = variant_names
|
||
return
|
||
# 【声明发射重入标记】本函数主体会重入自身:成员收集(CollectAnnAssignMember
|
||
# → ResolveAnnotationType → 泛型特化 list[T]/GSListNode[T] → _EmitClassLlvm
|
||
# → StructGen._generate_structs → _TryLoadStructFromStub(X)
|
||
# → _TryLoadClassMembersFromPyi(X) → 本函数(X))。首字段即指针/容器的类必现
|
||
# (llvmlite.Param 的 Attrs、match.MatchOr 的 patterns、stmts.Interactive 的
|
||
# body…),且每轮仅 4 帧:本函数 → _TryLoadStructFromStub → 成员加载 → 本函数。
|
||
# 两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同,失败时键还会被
|
||
# 清除(_TryLoadStructFromStub 的"仍 opaque ⇒ 删键重试"),缓存守卫挡不住
|
||
# ⇒ 无限递归(RecursionError 被上层 except 吞掉,表现为类型解析静默退化)。
|
||
# 登记 (ClassName, source_sha1);由 `_TryLoadStructFromStub` 里的"顺带加载成员"
|
||
# 那一步短路(该边是递归环的唯一闭合边:结构体 body 加载会连带成员加载 → 又回到
|
||
# 本函数);本函数自身**不**短路——重入的成员收集是"把表填起来"的唯一途径,
|
||
# 消费者(重入编译的其他模块方法体按名查字段)必须能拿到它。
|
||
# 放在 CEnum/REnum 早退(上方 return)之后:那两个分支不加载成员/结构体。
|
||
_emit_inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None)
|
||
if _emit_inprog is None:
|
||
_emit_inprog = set()
|
||
Gen._extern_decl_emit_in_progress = _emit_inprog
|
||
_emit_key: tuple = (ClassName, source_sha1)
|
||
_emit_inprog.add(_emit_key)
|
||
# 先初始化 class_methods/class_members 等容器,确保 _TryLoadStructFromStub
|
||
# 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods)
|
||
# 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32)
|
||
if ClassName not in Gen.class_members:
|
||
Gen.class_members[ClassName] = []
|
||
if ClassName not in Gen.class_member_defaults:
|
||
Gen.class_member_defaults[ClassName] = {}
|
||
if ClassName not in Gen.class_member_signeds:
|
||
Gen.class_member_signeds[ClassName] = {}
|
||
if ClassName not in Gen.class_member_bitfields:
|
||
Gen.class_member_bitfields[ClassName] = {}
|
||
if ClassName not in Gen.class_member_byteorders:
|
||
Gen.class_member_byteorders[ClassName] = {}
|
||
if ClassName not in Gen.class_member_bitoffsets:
|
||
Gen.class_member_bitoffsets[ClassName] = {}
|
||
if ClassName not in Gen.class_methods:
|
||
Gen.class_methods[ClassName] = []
|
||
# source_sha1 已在函数开头统一计算(作为分类缓存键),此处直接使用。
|
||
# 注意:REnum 分支(上方)会按 actual_module_name 优先重新推导并已 return。
|
||
# 提前注册 struct:使用导入的 source_sha1 创建 opaque struct,
|
||
# 避免后续 _TryLoadStructFromStub 触发嵌套编译时,StructGen 用本地 sha1
|
||
# 创建缺少基类字段(如 GSListNode.Next)的本地类型定义。
|
||
Gen._get_or_create_struct(ClassName, source_sha1=source_sha1, packed=IsPacked)
|
||
# Track which module defines this class (for cross-module name mangling)
|
||
if source_sha1:
|
||
if not hasattr(Gen, 'class_sha1_map'):
|
||
Gen.class_sha1_map = {}
|
||
Gen.class_sha1_map[ClassName] = source_sha1
|
||
# --- 预扫描预注册:修复编译时序问题 ---
|
||
# _TryLoadStructFromStub → _parse_llvm_type → _specialize_generic_class
|
||
# 可能触发 list[str].__new__ 编译(其中调用 pool.alloc(48)),
|
||
# 此时 MemManager 尚未注册到虚表,导致直接调用返回 NULL → 段错误。
|
||
# 修复:在 _TryLoadStructFromStub 之前预扫描方法列表并提前注册虚表。
|
||
_has_methods_prescan = False
|
||
# PEP 695 泛型类的方法声明必须跳过:方法参数类型 T(如 list[T].append(item: T))
|
||
# 会被解析为 opaque struct,LLVM 报 "invalid type for function argument"。
|
||
# 字段(AnnAssign)仍正常加载,确保继承字段(如 GSListNode.Next)可被展平。
|
||
if not IsGenericClass:
|
||
# 虚表槽筛选:方法级 `novirtual def`(⇒ @t.NoVTable)不进槽、方法级
|
||
# `virtual def`(⇒ @t.CVTable)进槽——判据为 SymbolUtils.IsVtableSlotMethod
|
||
# (唯一规则源,与定义端 HandlesClassDef / TPV _should_method_be_virtual 同口径),
|
||
# 保证跨模块虚表槽序与定义模块逐槽一致。
|
||
_VtByClassLevel: bool = _VtableByClassLevelForImport(Node, Gen)
|
||
for _item in Node.body:
|
||
if isinstance(_item, ast.FunctionDef):
|
||
_has_methods_prescan = True
|
||
_MethodName = _item.name
|
||
# 构造函数(__new__/__init__/__before_init__)不放入 vtable,
|
||
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移
|
||
# (如 Module 有 __new__/__init__ 使 vtable=7,AST 无它们 vtable=5,
|
||
# 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。
|
||
if _MethodName in ('__new__', '__init__', '__before_init__'):
|
||
continue
|
||
_FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}"
|
||
_IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(_item, _VtByClassLevel)
|
||
if _IsSlot and _FuncFullName not in Gen.class_methods[ClassName]:
|
||
Gen.class_methods[ClassName].append(_FuncFullName)
|
||
if _has_methods_prescan and IsCVTable:
|
||
Gen._cross_module_vtable_classes.add(ClassName)
|
||
Gen.class_vtable.add(ClassName)
|
||
# 首次加载就传入 source_sha1,避免从其他模块的 output stub 中加载到错误的结构体定义
|
||
# (如 HandlesExprCall.py 的 output stub 中 43f27dda2e5b5923.Value 只有 5 字段,
|
||
# 而正确的 aaf8ba449d1e64c1.Value 有 6 字段)
|
||
self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1)
|
||
# 如果 _get_or_create_struct 因 sha1 冲突创建了新的 opaque struct,
|
||
# 需用 source_sha1 重新加载正确的 stub body。
|
||
if source_sha1:
|
||
self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1)
|
||
has_methods = False
|
||
# 解析 __provides__/__requires__/__require_must__ 编译期元数据(跨模块加载)
|
||
for item in Node.body:
|
||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
field_name: str = item.target.id
|
||
if field_name in ('__provides__', '__requires__', '__require_must__') and item.value:
|
||
values: list[str] = []
|
||
if isinstance(item.value, ast.List):
|
||
for elt in item.value.elts:
|
||
if isinstance(elt, ast.Constant) and isinstance(elt.value, str):
|
||
values.append(elt.value)
|
||
if field_name == '__provides__':
|
||
Gen.class_provides[ClassName] = values
|
||
elif field_name == '__requires__':
|
||
Gen.class_requires[ClassName] = values
|
||
elif field_name == '__require_must__':
|
||
Gen.class_require_must[ClassName] = values
|
||
# 字段收集中保护(与 _EmitClassLlvm 的 _collecting_members_set 同源):
|
||
# 本节的成员解析会重入结构体发射 —— CollectAnnAssignMember →
|
||
# ResolveAnnotationType → HandlesTypeMerge._HandleSubscript → CollectAll →
|
||
# _ResolveGenericSlice → _specialize_generic_class → _EmitClassLlvm →
|
||
# _generate_structs(),而此刻 class_members[ClassName] 只含"已扫到的
|
||
# 自有字段"(继承字段展平尚未执行,见下方 _all_inherited 段),
|
||
# 且 llvmlite 的 set_body 只允许调用一次 → 残缺布局被永久固化:
|
||
# BasicBlock.Lines: rawptr[GSList[Line]] → body 只到 Name 即止
|
||
# Function.Params: rawptr[GSList[Param]] → body 只到 RetTy 即止
|
||
# Line/Param 等则整类丢失继承字段 Next(GEP 索引整体前移一位)。
|
||
# 置入保护后重入的 _generate_structs 会跳过本类(StructGen.py:461),
|
||
# 待本节收集+展平完成后由下方显式 _generate_structs() 一次性 set_body。
|
||
Gen._collecting_members_set.add(ClassName)
|
||
_existing_member_names = {m[0] for m in Gen.class_members[ClassName]}
|
||
for item in Node.body:
|
||
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
|
||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
if item.target.id in ('__provides__', '__requires__', '__require_must__'):
|
||
continue
|
||
parsed = self.CollectAnnAssignMember(item, Gen)
|
||
if parsed:
|
||
VarName, MemberType, TypeInfo = parsed
|
||
if TypeInfo is None:
|
||
if VarName not in _existing_member_names:
|
||
Gen.class_members[ClassName].append((VarName, MemberType))
|
||
_existing_member_names.add(VarName)
|
||
Gen.class_member_signeds[ClassName][VarName] = None
|
||
Gen.class_member_bitfields[ClassName][VarName] = 0
|
||
else:
|
||
if TypeInfo.IsBitField:
|
||
Gen.class_member_bitfields[ClassName][VarName] = TypeInfo.BitWidth
|
||
else:
|
||
if VarName not in _existing_member_names:
|
||
Gen.class_members[ClassName].append((VarName, MemberType))
|
||
_existing_member_names.add(VarName)
|
||
Gen.class_member_bitfields[ClassName][VarName] = 0
|
||
# 与 HandlesClassDef 对齐:联合类型字段(如 `list[str] | t.CPtr`)
|
||
# 记录泛型特化名到 class_member_element_class。否则跨模块 import
|
||
# 的类(如 TypeGenBase.CTypedefNames)缺此记录,_HandleLenLlvm
|
||
# 无法识别字段为 list → len() 误编译为 strlen → stdint 崩溃。
|
||
if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8:
|
||
if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr):
|
||
element_class: str | None = FindStructNameInAnnotationWithContext(
|
||
item.annotation, self.Trans, Gen)
|
||
if not element_class and TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs:
|
||
element_class = TypeInfo.Name
|
||
if element_class:
|
||
if ClassName not in Gen.class_member_element_class:
|
||
Gen.class_member_element_class[ClassName] = {}
|
||
Gen.class_member_element_class[ClassName][VarName] = element_class
|
||
elif (isinstance(MemberType, ir.PointerType)
|
||
and isinstance(MemberType.pointee, ir.IdentifiedStructType)
|
||
and isinstance(item.annotation, ast.BinOp)
|
||
and isinstance(item.annotation.op, ast.BitOr)):
|
||
spec_in_anno: str | None = FindStructNameInAnnotationWithContext(
|
||
item.annotation, self.Trans, Gen)
|
||
if spec_in_anno and '[' in spec_in_anno:
|
||
if ClassName not in Gen.class_member_element_class:
|
||
Gen.class_member_element_class[ClassName] = {}
|
||
Gen.class_member_element_class[ClassName][VarName] = spec_in_anno
|
||
# Subscript 容器字段(list[X] / rawptr[list[X]],如 includes/ast
|
||
# 的 Call.args / Import.names: rawptr[list[rawptr[AST]]]):非 BinOp
|
||
# 联合,上面两分支不命中 → 容器身份丢失 → 字段方法调用(get/len…)
|
||
# 分派失败报 "Undefined method 'get' in class 'rawptr'"(Converter.vp)。
|
||
# 用与特化生成端同源的 _resolve_type_arg_str 构造 element_class,
|
||
# 键名与 Gen.structs 中 list 特化名保持一致。
|
||
if isinstance(item.annotation, ast.Subscript):
|
||
_ann_sub_s: ast.Subscript = item.annotation
|
||
_subv_s: ast.AST = _ann_sub_s.value
|
||
_subname_s: str = getattr(_subv_s, 'id', None) or getattr(_subv_s, 'attr', '')
|
||
_elem_slice_s: ast.AST | None = None
|
||
if _subname_s == 'list':
|
||
_elem_slice_s = _ann_sub_s.slice
|
||
elif _subname_s == 'rawptr':
|
||
_slice_s: ast.AST = _ann_sub_s.slice
|
||
if isinstance(_slice_s, ast.Subscript):
|
||
# rawptr[泛型容器](rawptr[list[X]] / rawptr[GSList[Param]]):
|
||
# 容器身份 = 泛型特化名。list 继续走 _elem_slice_s 统一登记;
|
||
# 其余泛型容器直接登记 element_class(阻塞 B 根因链一环:
|
||
# 原实现只认内层 list,rawptr[GSList[Param]] 在此空转并跳过
|
||
# 裸结构分支 → element_class 缺失 → 字段/局部变量方法分派
|
||
# 丢失容器身份,FakeDuck 错绑 LinkedNode.append 写穿)。
|
||
_inner_sub_s: ast.Subscript = _slice_s
|
||
_iname_s: str = getattr(_inner_sub_s.value, 'id', None) or getattr(_inner_sub_s.value, 'attr', '')
|
||
if _iname_s == 'list':
|
||
_elem_slice_s = _inner_sub_s.slice
|
||
else:
|
||
_ec_gs: str | None = None
|
||
if _iname_s:
|
||
try:
|
||
_ec_gs = f'{_iname_s}[{self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_s.slice)}]'
|
||
except Exception:
|
||
_ec_gs = None
|
||
if _ec_gs:
|
||
if ClassName not in Gen.class_member_element_class:
|
||
Gen.class_member_element_class[ClassName] = {}
|
||
Gen.class_member_element_class[ClassName][VarName] = _ec_gs
|
||
else:
|
||
# rawptr[裸结构](如 rawptr[Token]):跨模块 stub 会把自引用/类内
|
||
# 指针字段退化为 i8*,链式成员访问(a.b.c)需 element_class 恢复
|
||
# struct 身份走 char*→struct cast。HEAD 态 `Token | t.CPtr` 联合
|
||
# 由上方 BinOp 分支登记;rawptr 重构后此分支补齐(阻塞 A 根因)。
|
||
# 与 HandlesClassDef 的 rawptr 分支对称。
|
||
_slc_name_s: str | None = None
|
||
if isinstance(_slice_s, ast.Name):
|
||
_slc_name_s = _slice_s.id
|
||
elif isinstance(_slice_s, ast.Attribute):
|
||
_slc_name_s = _slice_s.attr
|
||
_ec_target_s: str | None = None
|
||
if _slc_name_s:
|
||
if _slc_name_s in Gen.structs:
|
||
_ec_target_s = _slc_name_s
|
||
else:
|
||
_st_sha1_s: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_slc_name_s)
|
||
if _st_sha1_s and f'{_st_sha1_s}.{_slc_name_s}' in Gen.structs:
|
||
_ec_target_s = f'{_st_sha1_s}.{_slc_name_s}'
|
||
else:
|
||
# 短名模糊扫描(对齐 HandlesAnnAssign 变量侧
|
||
# 补偿 L597-602):跨模块 `mod.Class` 形态的
|
||
# rawptr[Class] 字段在 class_sha1_map 尚无该短名
|
||
# 时上面两路全 miss → element_class 静默缺失 →
|
||
# 字段方法调用(factory.PermPool.alloc)链尾类名
|
||
# 丢失 → TPC 调用分派返回 i32 0 哨兵 → 误报
|
||
# "Unknown module: ...PermPool"。
|
||
for _ec_k_s in Gen.structs:
|
||
_ec_short_s: str = _ec_k_s.split('.')[-1] if '.' in _ec_k_s else _ec_k_s
|
||
if _ec_short_s == _slc_name_s:
|
||
_ec_target_s = _ec_k_s
|
||
break
|
||
if _ec_target_s:
|
||
if ClassName not in Gen.class_member_element_class:
|
||
Gen.class_member_element_class[ClassName] = {}
|
||
Gen.class_member_element_class[ClassName][VarName] = _ec_target_s
|
||
if _elem_slice_s is not None:
|
||
_ec_s: str | None = None
|
||
try:
|
||
_ec_s = f'list[{self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_s)}]'
|
||
except Exception:
|
||
_ec_s = None
|
||
if _ec_s:
|
||
if ClassName not in Gen.class_member_element_class:
|
||
Gen.class_member_element_class[ClassName] = {}
|
||
Gen.class_member_element_class[ClassName][VarName] = _ec_s
|
||
if TypeInfo.ByteOrder:
|
||
Gen.class_member_byteorders[ClassName][VarName] = TypeInfo.ByteOrder
|
||
else:
|
||
Gen.class_member_byteorders[ClassName][VarName] = ""
|
||
if item.value:
|
||
const = self._BuildScalarConstant(item.value, MemberType, Gen)
|
||
if const:
|
||
Gen.class_member_defaults[ClassName][VarName] = const
|
||
elif isinstance(item, ast.FunctionDef) and not IsGenericClass:
|
||
# PEP 695 泛型类跳过方法声明:T 参数会变成 opaque struct 触发
|
||
# "invalid type for function argument"。字段已由上面的 AnnAssign
|
||
# 分支加载,继承字段(如 GSListNode.Next)由后面的展平逻辑补充。
|
||
has_methods = True
|
||
MethodName = item.name
|
||
FuncFullName = f"{ClassName}.__init__" if MethodName == "__init__" else f"{ClassName}.__call__" if MethodName == "__call__" else f"{ClassName}.{MethodName}"
|
||
# 构造函数(__new__/__init__/__before_init__)不放入 vtable,
|
||
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。
|
||
# register_method 内部也会过滤,此处仅跳过 class_methods.append。
|
||
# 方法级修饰符(virtual def/novirtual def)⇒ 是否占槽按
|
||
# SymbolUtils.IsVtableSlotMethod(唯一规则源)判定,与定义模块逐槽一致。
|
||
_IsSlot2: bool = (not IsCVTable) or IsVtableSlotMethod(
|
||
item, _VtableByClassLevelForImport(Node, Gen))
|
||
if MethodName not in ('__new__', '__init__', '__before_init__'):
|
||
if _IsSlot2 and FuncFullName not in Gen.class_methods[ClassName]:
|
||
Gen.class_methods[ClassName].append(FuncFullName)
|
||
Gen.register_method(ClassName, FuncFullName, _IsSlot2)
|
||
is_item_static = any(isinstance(d, ast.Name) and d.id == 'staticmethod' for d in item.decorator_list)
|
||
is_item_property = any(isinstance(d, ast.Name) and d.id == 'property' for d in item.decorator_list)
|
||
is_item_classmethod = any(isinstance(d, ast.Name) and d.id == 'classmethod' for d in item.decorator_list)
|
||
is_item_prop_setter = any(isinstance(d, ast.Attribute) and d.attr == 'setter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list)
|
||
is_item_prop_getter = any(isinstance(d, ast.Attribute) and d.attr == 'getter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list)
|
||
is_item_prop_deleter = any(isinstance(d, ast.Attribute) and d.attr == 'deleter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list)
|
||
# setter/deleter 使用不同的函数名后缀
|
||
DeclFuncName = FuncFullName
|
||
if is_item_prop_setter:
|
||
DeclFuncName = FuncFullName + '$set'
|
||
elif is_item_prop_deleter:
|
||
DeclFuncName = FuncFullName + '$del'
|
||
if DeclFuncName not in Gen.functions:
|
||
FuncDeclNode = ast.FunctionDef(
|
||
name=DeclFuncName,
|
||
args=item.args,
|
||
body=item.body,
|
||
decorator_list=item.decorator_list,
|
||
returns=item.returns
|
||
)
|
||
try:
|
||
self._EmitExternalFuncDeclLlvm(FuncDeclNode, Gen, is_class_method=not is_item_static and not is_item_classmethod, source_module_name=module_name)
|
||
except Exception as e:
|
||
raise
|
||
func_meta = FuncMeta.NONE
|
||
if is_item_static:
|
||
func_meta |= FuncMeta.STATIC_METHOD
|
||
if is_item_property:
|
||
func_meta |= FuncMeta.PROPERTY_GETTER
|
||
if is_item_classmethod:
|
||
func_meta |= FuncMeta.CLASS_METHOD
|
||
if is_item_prop_setter:
|
||
func_meta |= FuncMeta.PROPERTY_SETTER
|
||
if is_item_prop_getter:
|
||
func_meta |= FuncMeta.PROPERTY_GETTER
|
||
if is_item_prop_deleter:
|
||
func_meta |= FuncMeta.PROPERTY_DELETER
|
||
# setter/deleter 的 SymKey 使用带后缀的函数名
|
||
SymKey = DeclFuncName
|
||
if not self.Trans.SymbolTable.has(SymKey):
|
||
FuncInfo = CTypeInfo()
|
||
FuncInfo.Name = SymKey
|
||
FuncInfo.IsFunction = True
|
||
FuncInfo.MetaList = func_meta
|
||
self.Trans.SymbolTable.insert(SymKey, FuncInfo)
|
||
else:
|
||
existing = self.Trans.SymbolTable[SymKey]
|
||
if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE:
|
||
existing.MetaList = func_meta
|
||
# setter/deleter: 同时在原始 PropKey(不带后缀)下注册 MetaList
|
||
if is_item_prop_setter or is_item_prop_deleter:
|
||
base_existing = self.Trans.SymbolTable.lookup(FuncFullName)
|
||
if base_existing:
|
||
if func_meta != FuncMeta.NONE:
|
||
base_existing.MetaList = base_existing.MetaList | func_meta
|
||
else:
|
||
PropInfo = CTypeInfo()
|
||
PropInfo.Name = FuncFullName
|
||
PropInfo.IsFunction = True
|
||
PropInfo.MetaList = func_meta
|
||
self.Trans.SymbolTable.insert(FuncFullName, PropInfo)
|
||
# --- 跨模块继承字段展平 ---
|
||
# _EmitExternalClassDeclLlvm 只添加了类自身定义的字段(来自 .pyi),
|
||
# 没有展平父类(如 GSListNode[T])的继承字段,导致 class_members 缺少
|
||
# 继承字段,_get_member_offset 返回错误偏移。
|
||
# 修复:先尝试从已加载的父类获取真实字段名;若父类未加载,
|
||
# 则用 struct body 元素数推断继承字段数并补充占位条目 __inherited_N。
|
||
_ParentClass: str | None = None
|
||
_ParentClasses: list[str] = []
|
||
if Node.bases:
|
||
for _base in Node.bases:
|
||
_base_name: str | None = None
|
||
if hasattr(_base, 'id'):
|
||
_base_name = _base.id
|
||
elif hasattr(_base, 'attr'):
|
||
_base_name = _base.attr
|
||
elif isinstance(_base, ast.Subscript):
|
||
_spec_name: str | None = self.Trans.ExprCallHandle._ResolveGenericSlice(_base)
|
||
if _spec_name:
|
||
_base_name = _spec_name
|
||
else:
|
||
# _ResolveGenericSlice 失败(泛型基类未在当前模块注册为模板,
|
||
# 常见于跨模块导入:如 HandlesExprCall.py 导入 Value(GSListNode[Value]),
|
||
# 但 GSListNode 定义在 includes/linkedlist.py,尚未编译)。
|
||
# 直接从 AST 提取泛型基类名(如 GSListNode[Value] -> GSListNode),
|
||
# 后续继承字段展平逻辑会从 .pyi 加载该基类的字段。
|
||
if isinstance(_base.value, ast.Name):
|
||
_base_short_name: str = _base.value.id
|
||
elif isinstance(_base.value, ast.Attribute):
|
||
_base_short_name: str = _base.value.attr
|
||
else:
|
||
continue
|
||
# 尝试手动构造特化名并检查是否已有特化 class_members。
|
||
# 这解决了 _ResolveGenericSlice 失败但特化已通过其他路径
|
||
# (如 _parse_llvm_type)触发的情况。特化 class_members 中
|
||
# 的 T 已被正确替换为具体类型(如 BasicBlock),避免使用
|
||
# 未特化的 GSListNode 导致 T* 占位类型残留。
|
||
_slice_name: str | None = None
|
||
if isinstance(_base.slice, ast.Name):
|
||
_slice_name = _base.slice.id
|
||
elif isinstance(_base.slice, ast.Attribute):
|
||
_slice_name = _base.slice.attr
|
||
elif isinstance(_base.slice, ast.Subscript):
|
||
# 嵌套泛型(如 GSList[GSListNode[T]]),暂不处理
|
||
pass
|
||
_manual_spec_name: str | None = None
|
||
if _slice_name is not None:
|
||
_manual_spec_name = f"{_base_short_name}[{_slice_name}]"
|
||
if _manual_spec_name is not None and _manual_spec_name in Gen.class_members:
|
||
# 特化 class_members 已存在,直接使用(T 已被替换)
|
||
_base_name = _manual_spec_name
|
||
else:
|
||
_base_name = _base_short_name
|
||
else:
|
||
continue
|
||
if _base_name == ClassName:
|
||
continue
|
||
if _base_name in ('CVTable', 'Object', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'):
|
||
continue
|
||
# 多继承:收集所有真实父类(声明顺序),不再 break。
|
||
# 总是记录父类名,即使父类 class_members 未加载。
|
||
# _get_member_offset 会沿 class_parent 链回退查找继承字段。
|
||
_ParentClasses.append(_base_name)
|
||
if _ParentClass is None:
|
||
_ParentClass = _base_name
|
||
if not hasattr(Gen, 'class_parent'):
|
||
Gen.class_parent = {}
|
||
Gen.class_parent[ClassName] = _ParentClass
|
||
if _ParentClasses:
|
||
if not hasattr(Gen, 'class_parents'):
|
||
Gen.class_parents = {}
|
||
Gen.class_parents[ClassName] = _ParentClasses
|
||
# 治本修复:在继承展开前,尝试从 stub/.pyi 加载父类的 class_members。
|
||
# 确保子类 class_members 能正确展平父类真实字段名(而非 __inherited_N 占位符),
|
||
# 避免 struct body 从不完整 class_members 生成导致 GEP 越界。
|
||
# 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。
|
||
_any_parent_loaded: bool = False
|
||
_all_inherited: list[tuple] = []
|
||
# 展平完整性判定(供 `_members_flattened` 登记):仅当"每个声明父类都已在
|
||
# class_members 中就绪且其自身已展平",或"占位符路径已按 struct 槽位补齐
|
||
# 继承前缀"时才为 True。未完成(典型:声明期 struct 尚 opaque / 父类所在
|
||
# 模块 stub 未就绪)时不登记,后续查询会重跑本节把继承前缀补齐
|
||
# —— 使展平结果与"父类/struct 就绪的先后顺序"无关。
|
||
_flat_complete: bool = not _ParentClasses
|
||
_placeholder_applied: bool = False
|
||
# 按继承源去重的跨父类共享 seen 集合(必须在 for 循环外,跨所有父类
|
||
# 共享——否则每个父类新建 seen,菱形祖先(如 ClassGenBase)被每个
|
||
# 继承它的 ClassGen* 父类各展平一次 → 重复(Generator 曾 707 字段)
|
||
_seen_flat_cls: set[str] = set()
|
||
for _ParentClass in _ParentClasses:
|
||
if not _ParentClass:
|
||
continue
|
||
_parent_members_check: list = Gen.class_members.get(_ParentClass, [])
|
||
_parent_all_placeholders: bool = bool(_parent_members_check) and all(
|
||
n.startswith('__inherited_') for n, _ in _parent_members_check
|
||
)
|
||
# 2026-08-18 关键修复:无条件 force 重载父类成员(此前仅当成员为空/
|
||
# 全占位时才加载)。并行 worker 中父类可能已存在"不完整"的成员表
|
||
# (此前某次展平时祖先未加载完成即冻结部分字段,guard 又阻止重载),
|
||
# 导致 class_members[Generator] 83~106 vs 114 不确定 → this_adjust
|
||
# 错位 → 继承方法 self 偏移错误 → 运行时越界崩溃。force=True 会先
|
||
# 清空再重建(缺啥补啥、顺序与定义模块一致),且 _EmitExternalClassDeclLlvm
|
||
# 对其祖先递归同样强制重载,最终所有 worker 展平结果确定性一致。
|
||
_old_parent_members: list = _parent_members_check
|
||
if _parent_all_placeholders:
|
||
Gen.class_members[_ParentClass] = []
|
||
_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) or {}
|
||
_parent_found: bool = False
|
||
# 已加载过的父类直接用其定义模块 sha1 强制重载(O(1)),
|
||
# 避免对每个父类线性扫描全部 ModuleSha1Map。
|
||
_parent_sha1: str | None = getattr(Gen, 'class_sha1_map', {}).get(_ParentClass)
|
||
if _parent_sha1:
|
||
try:
|
||
_parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, f"{_parent_sha1}.stub.ll", Gen, force=True)
|
||
except Exception:
|
||
_parent_found = False
|
||
else:
|
||
for _mod_name, _mod_sha1 in _sha1_map.items():
|
||
_stub_name: str = f"{_mod_sha1}.stub.ll"
|
||
try:
|
||
_parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, _stub_name, Gen, force=True)
|
||
except Exception:
|
||
_parent_found = False
|
||
if _parent_found:
|
||
break
|
||
_parent_members_check = Gen.class_members.get(_ParentClass, [])
|
||
if not _parent_found and _old_parent_members:
|
||
# 加载失败(父类不在任何 pyi 中,如泛型特化基类),回滚旧条目(有总比没有好)
|
||
Gen.class_members[_ParentClass] = _old_parent_members
|
||
if _ParentClass in Gen.class_members:
|
||
# 按继承源去重(与 HandlesClassDef._FlattenInherited 一致,2026-08-18):
|
||
# 菱形继承(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase,
|
||
# 而它们都继承 ClassGenBase)若直接拼接会重复展平 → 与 DeclarationGenerator
|
||
# stub struct 布局不一致 → 并行 worker 里 this_adjust 算不出偏移 → 继承
|
||
# 方法 self 不调整 → 运行时错位崩溃(register_reexport 写 0x3f)。
|
||
# 递归每类只处理一次,每类只贡献非祖先字段。
|
||
def _FlattenExt(cls_name: str) -> None:
|
||
if cls_name in _seen_flat_cls:
|
||
return
|
||
_seen_flat_cls.add(cls_name)
|
||
for _gp in Gen.class_parents.get(cls_name, []) or []:
|
||
_FlattenExt(_gp)
|
||
_anc_flat: set[str] = set()
|
||
_stk: list[str] = [cls_name]
|
||
_vis: set[str] = set()
|
||
while _stk:
|
||
_c = _stk.pop()
|
||
for _gp2 in Gen.class_parents.get(_c, []) or []:
|
||
if _gp2 in _vis:
|
||
continue
|
||
_vis.add(_gp2)
|
||
_stk.append(_gp2)
|
||
for _mn2, _mt2 in Gen.class_members.get(_gp2, []) or []:
|
||
_anc_flat.add(_mn2)
|
||
for _mn3, _mt3 in Gen.class_members.get(cls_name, []) or []:
|
||
if _mn3 == '__vtable__':
|
||
continue
|
||
if _mn3 in _anc_flat:
|
||
continue
|
||
_all_inherited.append((_mn3, _mt3))
|
||
_pd3: dict = dict(Gen.class_member_defaults.get(cls_name, {}))
|
||
if _mn3 in _pd3:
|
||
Gen.class_member_defaults[ClassName][_mn3] = _pd3[_mn3]
|
||
_FlattenExt(_ParentClass)
|
||
if _all_inherited:
|
||
# 防重复注册(2026-08-10):_EmitExternalClassDeclLlvm 可能被多个模块
|
||
# 加载路径重复调用(_LoadModuleDeclarationsFromFile / _LoadDeclarationsFromStubLlvm
|
||
# / _TryLoadClassMembersFromPyi 懒加载),此前调用已前置的父类字段会被再次
|
||
# 前置 → class_members 出现重复字段(如 BasicBlock: [Next, Next, Name, ...])
|
||
# → 与 struct elements 数量不匹配 → 属性偏移错乱。
|
||
# 【判据改为"前置段是否已等于祖先块"(2026-09-30)】旧判据是
|
||
# `nm2 not in {整表所有名字}`——它会把**与子类自有字段重名的继承项**删掉,
|
||
# 于是继承段与 body 的继承前缀失去一一对应:被删项**之后**的继承字段表内
|
||
# 下标整体前移 k(k = 被删项数),而查询侧回退路径用 `base + 表内下标`
|
||
# (FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。现场:
|
||
# TypeGenCType 重复声明 5 个父类已有字段后,继承字段 LastBuiltinsTypedefResult
|
||
# 下标 32→27,写到 i32 槽 → "指针隐式转整数已禁止"。
|
||
# 新判据(幂等且不删块内条目):只问"表头前 len(块) 项的名字序列是否等于
|
||
# 祖先块的名字序列"——相等 ⇒ 整块已前置(本次是重复调用)⇒ 不再前置;
|
||
# 否则整块前置。**任何情况下都不删块内条目**,因此"同名自有字段 + 同名继承项"
|
||
# 会并存(body 里本来就是两个槽,定义端 HandlesClassDef 同样两者都在)。
|
||
PrependInheritedBlock(Gen, ClassName, _all_inherited)
|
||
# 父类字段已处理(可能此前已前置),跳过占位符路径避免叠加
|
||
_any_parent_loaded = True
|
||
if not _any_parent_loaded and _ParentClasses:
|
||
# 父类未加载到 class_members(常见于泛型特化基类如 GSListNode[Value],
|
||
# 其特化不在 .pyi 中)。用 struct body 元素数推断继承字段数。
|
||
_st = Gen.structs.get(ClassName)
|
||
# 【必须先取快照】`elements` 不是稳定值:`StructGen._generate_structs` 判定
|
||
# "struct body 与 class_members 不一致"时会**主动置 `elements = None`**
|
||
# (StructGen.py:579/:590,为强制重新派生的既有机制)。本分支在下方
|
||
# `_ResolveInheritedFieldNames`(加载父类成员 → _TryLoadClassMembersFromPyi
|
||
# → _EmitExternalClassDeclLlvm → _generate_structs)里会重入,重入期间本类
|
||
# 的 `elements` 可能被置回 None ⇒ 若在配对时(:2526)才读 `_st.elements`,
|
||
# 就是对 None 下标:裸 `TypeError: 'NoneType' object is not subscriptable`
|
||
# (2026-09-29 现场:astaux.vp 编译期 `_EmitExternalClassDeclLlvm` 崩,
|
||
# 外层 :647 `except Exception` 只 WARN "忽略节点 ClassDef" 就 continue ⇒
|
||
# 该类声明被整条静默丢弃)。
|
||
# 快照与 `_num_elements` 同源(都是 guard 通过的这一份 body),二者必须
|
||
# 一致:槽位数与槽类型取自同一时刻的布局,不能一个用旧、一个用新。
|
||
if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None:
|
||
_elements_ph: list = list(_st.elements)
|
||
_num_elements = len(_elements_ph)
|
||
# 如果类有 vtable ptr(struct 首元素),需减去 1
|
||
# 否则 vtable ptr 会被误当成 __inherited_0 占位条目,
|
||
# 导致子类继承时多出一个字段(如 Name(AST) 从 9 字段变成 10)
|
||
_has_vtable_ptr = IsCVTable or (has_methods and not IsNoVTable)
|
||
if _has_vtable_ptr and _num_elements > 0:
|
||
_num_elements -= 1
|
||
# struct 槽位起点:有 vtable 指针时首元素是 vtable,继承字段从元素 1 起
|
||
# (上面已把 vtable 从 _num_elements 扣掉,配对槽位必须同步右移一位,
|
||
# 否则会把 vtable 指针的类型当成第一个继承字段的类型)。
|
||
_slot0_ph: int = 1 if _has_vtable_ptr else 0
|
||
_num_members = len(Gen.class_members[ClassName])
|
||
if _num_elements > _num_members:
|
||
_num_inherited = _num_elements - _num_members
|
||
# 继承字段的**真实名字**:按声明顺序沿继承链取父类声明中的字段名
|
||
# (父类可能是泛型特化 `GSListNode[ParamNode]` —— 特化体不在任何
|
||
# .vpi 中,回退用模板名 `GSListNode` 加载其成员),与 struct 槽位
|
||
# 按序配对。这样展平结果直接是 `('Next', <槽位类型>)`,属性访问
|
||
# 命中 class_members 真实条目,**无需任何查找期回退**。
|
||
# 名字取不到时(父类声明也不可得)才退回 __inherited_N 占位名,
|
||
# 仅保证偏移正确。
|
||
_inh_names: list[str] = self._ResolveInheritedFieldNames(Gen, _ParentClasses, _num_inherited)
|
||
_inherited_ph: list[tuple] = []
|
||
for _i_ph in range(_num_inherited):
|
||
_nm_ph: str = _inh_names[_i_ph] if _i_ph < len(_inh_names) else f'__inherited_{_i_ph}'
|
||
_inherited_ph.append((_nm_ph, _elements_ph[_slot0_ph + _i_ph]))
|
||
# 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致
|
||
Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName]
|
||
_placeholder_applied = True
|
||
if _ParentClasses:
|
||
# 完整展平的判据:占位符路径已按 struct 槽位补齐继承前缀,或
|
||
# 每个声明父类都已在 class_members 中就绪且其自身已展平。
|
||
_flat_complete = _placeholder_applied or all(
|
||
(_pc in Gen.class_members and _pc in Gen._members_flattened)
|
||
for _pc in _ParentClasses if _pc
|
||
)
|
||
# 成员收集 + 继承展平完成 → 解除收集中保护,并用完整 class_members 设置 body。
|
||
# 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的
|
||
# _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque
|
||
# (opaque 结构体上的 GEP 是非法 IR,llvm-as 会直接报错),而残缺布局正是
|
||
# 本保护要消除的目标。
|
||
Gen._collecting_members_set.discard(ClassName)
|
||
Gen._generate_structs()
|
||
if _flat_complete:
|
||
# 登记"该表已按父类展平",供 _TryLoadClassMembersFromPyi 的 guard 与
|
||
# _member_base 的查询前补齐使用。未登记 ⇒ 后续任一查询都可重跑本节补齐。
|
||
Gen._members_flattened.add(ClassName)
|
||
# ============================================================
|
||
# 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位),
|
||
# 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只
|
||
# 注册自身方法(_has_methods_prescan),vtable 槽位会错位 →
|
||
# 虚分派调用错误函数(如 MemBuddy.alloc 被解析到槽 13=_fl_find_and_remove
|
||
# → 返回 None → "Generator alloc failed")。
|
||
# ============================================================
|
||
if _ParentClasses:
|
||
_all_parent_methods: list[str] = []
|
||
_parent_method_names_seen: set[str] = set()
|
||
for _pcls in _ParentClasses:
|
||
for _pm in list(Gen.class_methods.get(_pcls, [])):
|
||
_pm_name: str = _pm.split('.')[-1] if '.' in _pm else _pm
|
||
if _pm_name in ('__new__', '__init__', '__before_init__'):
|
||
continue
|
||
if _pm_name in _parent_method_names_seen:
|
||
continue
|
||
_parent_method_names_seen.add(_pm_name)
|
||
_all_parent_methods.append(_pm)
|
||
if _all_parent_methods:
|
||
_child_method_map: dict[str, str] = {}
|
||
for _m in Gen.class_methods.get(ClassName, []):
|
||
_mn: str = _m.split('.')[-1] if '.' in _m else _m
|
||
_child_method_map[_mn] = _m
|
||
# 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册)。
|
||
# 方法级 `novirtual def`(⇒ @t.NoVTable)不占槽,**不得**用它覆盖父类槽位
|
||
# ——与 TPV _collect_vtable_methods 的 child_nvt 分支同口径(子类标记
|
||
# NoVTable 时槽位仍指向实际定义类的实现)。
|
||
_VtByClassLevelC: bool = _VtableByClassLevelForImport(Node, Gen)
|
||
for _item_m in getattr(Node, 'body', []):
|
||
if isinstance(_item_m, ast.FunctionDef):
|
||
_mn2: str = _item_m.name
|
||
if _mn2 in ('__new__', '__init__', '__before_init__'):
|
||
continue
|
||
if not IsVtableSlotMethod(_item_m, _VtByClassLevelC):
|
||
continue
|
||
_child_method_map[_mn2] = f"{ClassName}.{_mn2}"
|
||
_new_methods: list[str] = []
|
||
_covered_names: set[str] = set()
|
||
for _pm2 in _all_parent_methods:
|
||
_mname2: str = _pm2.split('.')[-1] if '.' in _pm2 else _pm2
|
||
if _mname2 in _child_method_map:
|
||
_new_methods.append(_child_method_map[_mname2])
|
||
else:
|
||
_new_methods.append(_pm2)
|
||
_covered_names.add(_mname2)
|
||
for _m3 in list(Gen.class_methods.get(ClassName, [])):
|
||
_mn3: str = _m3.split('.')[-1] if '.' in _m3 else _m3
|
||
if _mn3 not in _covered_names:
|
||
_new_methods.append(_m3)
|
||
Gen.class_methods[ClassName] = _new_methods
|
||
if has_methods:
|
||
if IsCVTable:
|
||
Gen._cross_module_vtable_classes.add(ClassName)
|
||
Gen.class_vtable.add(ClassName)
|
||
if IsNoVTable:
|
||
Gen._cross_module_novtable.add(ClassName)
|
||
# __before_init__ 对所有跨模块结构体声明(定义侧无条件生成,导入侧也应无条件声明)
|
||
NewFuncName = f'{ClassName}.__before_init__'
|
||
if not Gen._has_function(NewFuncName):
|
||
# 复用前面已查找的 source_sha1(优先 actual_module_name 再 module_name),
|
||
# 通过 class_sha1_map 获取,避免此处只查 module_name 导致子模块(如 w32.win32file)查找失败
|
||
source_sha1 = getattr(Gen, 'class_sha1_map', {}).get(ClassName)
|
||
MangledName = Gen._mangle_name(NewFuncName) if not source_sha1 else f"{source_sha1}.{NewFuncName}"
|
||
StructType = Gen.structs.get(ClassName)
|
||
if StructType is not None:
|
||
StructPtrType = ir.PointerType(StructType)
|
||
NewFuncType = ir.FunctionType(ir.VoidType(), [StructPtrType])
|
||
# 走唯一幂等入口:查的是 NewFuncName 而建的是 MangledName,二者不同名
|
||
# ⇒ 此处若裸建,MangledName 已被 stub / 调用点占住时必抛
|
||
# llvmlite DuplicatedNameError。
|
||
NewFunc = Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType)
|
||
Gen.functions[NewFuncName] = NewFunc
|
||
bitfields = Gen.class_member_bitfields.get(ClassName, {})
|
||
if any(v > 0 for v in bitfields.values()):
|
||
current_bit_offset = 0
|
||
for name, _ in Gen.class_members.get(ClassName, []):
|
||
bw = bitfields.get(name, 0)
|
||
Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset
|
||
current_bit_offset += bw
|
||
# 声明发射结束:解除重入标记(与上方 add 配对)。
|
||
_emit_inprog.discard(_emit_key)
|
||
|
||
def _build_stub_index(self) -> None:
|
||
"""扫描所有 .stub.ll 文件构建 class_name → 条目列表索引
|
||
|
||
一次扫描替代 _TryLoadStructFromStub 的 609 次全量扫描。
|
||
增量策略:记录已解析文件集合 _stub_index_scanned,单次编译进程内
|
||
stub 文件只增不减(output 逐步填充),只增量解析新增文件;
|
||
无新增文件时仅做一次目录集合比较即返回(省去原签名失效后的全量重扫)。
|
||
|
||
零 syscall 快路径:本函数由 _TryLoadStructFromStub 的**每次索引 miss**
|
||
调用(profile:238k 次),而"stub 集合是否变化"由写入方显式置脏
|
||
(见 MarkStubSetDirty)⇒ 未置脏时直接返回,不再做
|
||
2× isdir + 2× listdir + 全量 endswith/join(这三项在 profile 里合计
|
||
约占总时长 30%:nt._path_isdir 491s、nt.listdir 183s、
|
||
np.join/splitroot/endswith ≈ 180s)。
|
||
"""
|
||
if not ImportHandle._stub_set_dirty:
|
||
return
|
||
ProjectRoot = self._find_project_root()
|
||
temp_dir = os.path.join(ProjectRoot, 'temp')
|
||
output_dir = os.path.join(ProjectRoot, 'output')
|
||
if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir):
|
||
return
|
||
|
||
# 收集当前 stub 文件集合(绝对路径,仅文件名不读内容)
|
||
current_files: set[str] = set()
|
||
for scan_dir in (output_dir, temp_dir):
|
||
if not os.path.isdir(scan_dir):
|
||
continue
|
||
for filename in os.listdir(scan_dir):
|
||
if filename.endswith('.stub.ll'):
|
||
current_files.add(os.path.join(scan_dir, filename))
|
||
|
||
# 无新增文件 → 索引最新,清脏(下次 miss 走零 syscall 快路径)
|
||
if current_files <= self._stub_index_scanned:
|
||
ImportHandle._stub_set_dirty = False
|
||
return
|
||
|
||
# 只增量解析新增文件(首次调用时全量)
|
||
for stub_path in sorted(current_files - self._stub_index_scanned):
|
||
filename = os.path.basename(stub_path)
|
||
# 预检查文件存在性:os.listdir 与 open 之间文件可能被 --clean 或
|
||
# 并发写入删除(独立 profile 脚本曾触发 FileNotFoundError)
|
||
if not os.path.isfile(stub_path):
|
||
continue
|
||
is_output = stub_path.startswith(os.path.join(output_dir, ''))
|
||
file_sha1 = filename.replace('.stub.ll', '')
|
||
try:
|
||
with open(stub_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
for line in content.splitlines():
|
||
stripped = line.strip()
|
||
match = re.match(r'%"?((?:[a-f0-9]+\.)?([\w\[\]]+))"?\s*=\s*type\s*(.*)', stripped)
|
||
if match:
|
||
full_name = match.group(1)
|
||
short_name = match.group(2)
|
||
struct_body = match.group(3).strip()
|
||
# 索引键:short_name 和 full_name 都映射到同一条目
|
||
self._stub_index.setdefault(short_name, []).append((full_name, struct_body, file_sha1, is_output))
|
||
if full_name != short_name:
|
||
self._stub_index.setdefault(full_name, []).append((full_name, struct_body, file_sha1, is_output))
|
||
except FileNotFoundError:
|
||
# 竞态:isfile 检查后文件被删除,跳过即可(不应触发 strict raise)
|
||
continue
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"构建 stub 索引失败: {_e}", "Exception")
|
||
self._stub_index_scanned.add(stub_path)
|
||
# 增量解析完成 → 索引与目录一致,清脏(下次 miss 走零 syscall 快路径)
|
||
ImportHandle._stub_set_dirty = False
|
||
|
||
@staticmethod
|
||
def _ExternDeclEmitting(Gen: LlvmGeneratorMixin, class_name: str, source_sha1: str | None) -> bool:
|
||
"""该类(同名同源)的跨模块声明发射是否**正在进行中**(递归重入判定)。
|
||
|
||
标记由 `_EmitExternalClassDeclLlvm` 主体开头登记、末尾解除(键
|
||
`(ClassName, source_sha1)`)。定义模块 sha1 未知(None)时按类名保守判定:
|
||
None 与 '<sha1>' 两种形态可能指同一个类,宁可本次漏加载(由最外层收尾)
|
||
也不递归——`_EmitExternalClassDeclLlvm` → `_TryLoadStructFromStub(X)`
|
||
→ `_TryLoadClassMembersFromPyi(X)` → `_EmitExternalClassDeclLlvm(X)`
|
||
每轮仅 4 帧,且两条加载路径的缓存键(带/不带 sha1 前缀)互不相同、
|
||
失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住,
|
||
会一直递归到 RecursionError(被上层 except 吞掉 ⇒ 类型解析静默退化)。
|
||
"""
|
||
_inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None)
|
||
if not _inprog:
|
||
return False
|
||
if (class_name, source_sha1) in _inprog:
|
||
return True
|
||
if source_sha1 is None:
|
||
return any(_k[0] == class_name for _k in _inprog)
|
||
return False
|
||
|
||
def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None) -> None:
|
||
# 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义,
|
||
# 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致
|
||
# 跨模块 struct 类型对象无法分别 set_body(每个模块有独立的 self.structs)。
|
||
# 治本修复:当 source_sha1 指定时(同名类冲突情况),使用 sha1 前缀全名作为
|
||
# 缓存键,确保不同模块的同名类(如 stmts.py:Module 和 __module.py:Module)
|
||
# 不会互相阻止 stub 加载。
|
||
cache_key = (id(Gen), f"{source_sha1}.{class_name}" if source_sha1 else class_name)
|
||
if cache_key in self._struct_Load_cache:
|
||
return
|
||
# 先设置缓存防止递归重入,但如果加载失败则清除缓存允许后续重试
|
||
self._struct_Load_cache[cache_key] = True
|
||
|
||
# 使用 stub 索引替代全量扫描(609 次 × 100 文件 → 1 次扫描 + O(1) 查询)。
|
||
# 命中索引直接返回,不触发 _build_stub_index(避免每次调用做目录扫描);
|
||
# 仅在 miss 时才增量扫描(无新增文件时仅一次集合比较)。
|
||
entries = self._stub_index.get(class_name, [])
|
||
if not entries:
|
||
self._build_stub_index()
|
||
entries = self._stub_index.get(class_name, [])
|
||
|
||
if not entries:
|
||
del self._struct_Load_cache[cache_key]
|
||
return
|
||
|
||
# 选择最佳条目:
|
||
# 优先级 0: 当 source_sha1 指定时,优先选择 type_sha1 匹配的条目(治本修复:
|
||
# 同名类冲突时确保选择正确模块的 struct 定义)
|
||
# 优先级 1: 类型定义模块自身的 stub(file_sha1 == full_name 中的 sha1 前缀)
|
||
# 定义模块的 stub 包含完整的字段展平(含跨模块继承字段)
|
||
# 优先级 2: output(Phase2 生成)优于 temp(Phase1 生成)
|
||
# 优先级 3: 非 opaque(具体定义)优于 opaque
|
||
def _entry_priority(e: tuple[str, str, str, bool]) -> tuple[int, int, int, int]:
|
||
full_name, struct_body, file_sha1, from_output = e
|
||
type_sha1 = file_sha1
|
||
if '.' in full_name:
|
||
parts = full_name.split('.', 1)
|
||
if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]):
|
||
type_sha1 = parts[0]
|
||
sha1_match = 0 if (source_sha1 and type_sha1 == source_sha1) else 1
|
||
is_defining = 0 if type_sha1 == file_sha1 else 1
|
||
is_temp = 0 if from_output else 1
|
||
is_opaque = 0 if (struct_body and struct_body != 'opaque') else 1
|
||
return (sha1_match, is_defining, is_temp, is_opaque)
|
||
|
||
best_entry = min(entries, key=_entry_priority)
|
||
full_name, struct_body, file_sha1, from_output = best_entry
|
||
|
||
# Bug 1 修复:从 full_name 提取 sha1 前缀(类型定义模块的 sha1),
|
||
# 而非使用 file_sha1(stub 文件所在模块的 sha1,可能只是导入方而非定义方)。
|
||
# 例如 output/35ffd4b9fbd082d1.stub.ll (main.py) 中包含
|
||
# %"5cde8c6813a2b6f3.Function" = type {...},
|
||
# 此时 file_sha1='35ffd4b9fbd082d1' (main.py) 但类型实际定义在
|
||
# __function.py (sha1='5cde8c6813a2b6f3'),必须用后者作为 source_sha1。
|
||
actual_sha1 = file_sha1
|
||
if '.' in full_name:
|
||
parts = full_name.split('.', 1)
|
||
if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]):
|
||
actual_sha1 = parts[0]
|
||
|
||
is_packed = struct_body is not None and struct_body.startswith('<{') and struct_body.endswith('}>')
|
||
if struct_body and struct_body != 'opaque':
|
||
elem_types = []
|
||
if is_packed:
|
||
inner = struct_body[2:-1].strip()
|
||
else:
|
||
inner = struct_body.strip('{}').strip()
|
||
if inner:
|
||
for elem in inner.split(','):
|
||
elem = elem.strip()
|
||
if elem:
|
||
et = self._parse_llvm_type(elem, Gen, source_sha1=actual_sha1)
|
||
elem_types.append(et if et else ir.IntType(32))
|
||
try:
|
||
st = Gen._get_or_create_struct(class_name, source_sha1=actual_sha1, packed=is_packed)
|
||
if isinstance(st, ir.IdentifiedStructType) and st.is_opaque:
|
||
st.set_body(*elem_types)
|
||
if is_packed:
|
||
Gen.class_packed.add(class_name)
|
||
# 同步设置短名 struct 的 body:当存在 sha1 冲突时,_get_or_create_struct
|
||
# 返回 full_key struct,但代码可能通过短名引用旧 struct(由 setup_from_symbol_table
|
||
# 早期创建的 opaque 占位符,使用本地 sha1 前缀)。需要同步设置短名 struct
|
||
# 的 body,确保 GEP 指令能正确访问字段(修复跨模块类型定义冲突导致的
|
||
# 字段索引错位问题)。
|
||
short_st = Gen.structs.get(class_name)
|
||
if short_st is not None and short_st is not st and isinstance(short_st, ir.IdentifiedStructType) and short_st.is_opaque:
|
||
try:
|
||
short_st.set_body(*elem_types)
|
||
except Exception:
|
||
pass # set_body 可能因已设置而失败,忽略
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
stub_filename = actual_sha1 + '.stub.ll'
|
||
# 【递归环闭合边 · 重入短路】该类的声明发射尚未结束(`_EmitExternalClassDeclLlvm`
|
||
# 正在跑:见其主体开头的 `_extern_decl_emit_in_progress` 登记)⇒ 本调用是环内一环:
|
||
# 发射(X) → _TryLoadStructFromStub(X) →(此处)成员加载(X) → 发射(X) …
|
||
# 每轮仅 4 帧,且两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同、
|
||
# 失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住 ⇒ 无限递归。
|
||
# 短路对象**只限"顺带加载成员"这一步**:上面的 struct body 解析照常执行
|
||
# (发射主体依赖它提前 set_body —— 否则 llvmlite `__len__` 的
|
||
# `assert self.elements is not None` 直接炸,见 _EmitExternalFuncDeclLlvm
|
||
# 的 `if _new_struct:`);成员由外层发射收尾统一收集。
|
||
if not ImportHandle._ExternDeclEmitting(Gen, class_name, source_sha1):
|
||
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
|
||
# 根本修复:加载后 struct 仍为 opaque 时(如选中 'type opaque' 占位条目、
|
||
# 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久
|
||
# 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。
|
||
# 失败状态可重试,待 stub 索引刷新 / 定义文件就绪后再加载。
|
||
_final_st = Gen.structs.get(class_name)
|
||
if isinstance(_final_st, ir.IdentifiedStructType) and _final_st.is_opaque:
|
||
try:
|
||
del self._struct_Load_cache[cache_key]
|
||
except KeyError:
|
||
pass
|
||
|
||
def EnsureClassMembersFlattened(self, class_name: str, Gen: LlvmGeneratorMixin) -> None:
|
||
"""查询前补齐:确保 `class_members[class_name]` 已按父类展平(幂等、顺序无关)。
|
||
|
||
背景:`_EmitExternalClassDeclLlvm` 的展平只在"父类成员表 + 声明期 struct
|
||
body 都已就绪"时才能算出继承前缀;若当时 struct 仍 opaque(定义模块 stub
|
||
尚未生成)或父类未就绪,表就停在"自有字段",而长度随之与 struct 元素数
|
||
不再自洽(delta = 缺失槽数)。此后 `_TryLoadClassMembersFromPyi` 的原
|
||
guard(`len>0` 即短路)会把这份残缺表永久冻结 ⇒ 该类**自有字段**的 GEP
|
||
下标缺了继承前缀而整体前移(`ExceptHandler.type` 返回 1 而非 8)。
|
||
|
||
本方法是该冻结的解除点(与 `FuncGen._member_base` 的查询侧调用配套):
|
||
只有"未登记已展平 且 该类有父类 且 struct 与成员表长度不自洽(含 struct
|
||
尚未登记)"才重跑展平;对已展平类、无父类类(含 REnum 变体等非继承键)
|
||
是纯内存判断直接返回,真正重跑的类另按依赖签名去重(见下)。
|
||
|
||
以 `(struct 元素数, 成员表长度)` 作为"依赖状态签名":同一签名下补齐无效
|
||
时不再重复尝试(避免依赖长期不就绪时在热路径反复重建);依赖一旦变化
|
||
(struct 被 `_generate_structs` 修正、或表被别处改动)签名变化即重试。
|
||
"""
|
||
if Gen is None:
|
||
return
|
||
_flat: set | None = getattr(Gen, '_members_flattened', None)
|
||
if _flat is None or class_name in _flat:
|
||
return
|
||
_inprog: set | None = getattr(Gen, '_members_flatten_in_progress', None)
|
||
if _inprog is None or class_name in _inprog:
|
||
return
|
||
if class_name in (getattr(Gen, '_collecting_members_set', None) or ()):
|
||
# 该类的字段正在收集中(重入路径)⇒ 不在此处重跑展平,
|
||
# 交由收集完成后的 `_EmitExternalClassDeclLlvm` 正常收尾。
|
||
return
|
||
_parents: list = (getattr(Gen, 'class_parents', {}) or {}).get(class_name) or []
|
||
if not _parents:
|
||
# 无父类 ⇒ 展平平凡成立。表非空时登记(后续查询直接短路);表为空时
|
||
# 不登记、也不在此补齐(那是"表未加载",由 _get_member_offset 既有的
|
||
# 懒加载路径负责,不在本专项范围内)。
|
||
if Gen.class_members.get(class_name):
|
||
_flat.add(class_name)
|
||
return
|
||
_tbl: list = Gen.class_members.get(class_name) or []
|
||
_st = Gen.structs.get(class_name)
|
||
_num_elements: int = -1
|
||
if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None:
|
||
_num_elements = len(_st.elements)
|
||
if (_num_elements - len(_tbl)) in (0, 1):
|
||
# struct 布局与成员表长度自洽(无 vtable 槽 / 恰一个 vtable 槽)
|
||
# ⇒ 已展平:登记,后续查询直接短路。
|
||
_flat.add(class_name)
|
||
return
|
||
_sig: tuple[int, int] = (_num_elements, len(_tbl))
|
||
_sigs: dict = getattr(Gen, '_members_flatten_repair_sig', None)
|
||
if _sigs is None:
|
||
_sigs = {}
|
||
Gen._members_flatten_repair_sig = _sigs
|
||
if _sigs.get(class_name) == _sig:
|
||
# 同一依赖状态下补齐无效(父类/struct 仍未就绪)⇒ 不重复尝试。
|
||
return
|
||
_sigs[class_name] = _sig
|
||
if not hasattr(self, 'Trans') or self.Trans is None:
|
||
return
|
||
_sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(class_name)
|
||
_cands: list[str] = [f"{_sha1}.stub.ll"] if _sha1 else []
|
||
if not _cands:
|
||
_sha1_map: dict = getattr(Gen, 'ModuleSha1Map', {}) or {}
|
||
_cands = [f"{_s}.stub.ll" for _s in _sha1_map.values()]
|
||
_inprog.add(class_name)
|
||
try:
|
||
for _stub in _cands:
|
||
try:
|
||
if self._TryLoadClassMembersFromPyi(class_name, _stub, Gen):
|
||
break
|
||
except Exception:
|
||
continue
|
||
finally:
|
||
_inprog.discard(class_name)
|
||
|
||
def _ResolveInheritedFieldNames(self, Gen: LlvmGeneratorMixin,
|
||
ParentClasses: list[str], Limit: int) -> list[str]:
|
||
"""沿继承链取父类声明中的**真实字段名**(按声明顺序,跨父类去重)。
|
||
|
||
用途:`_EmitExternalClassDeclLlvm` 的继承展平在"父类 class_members 未就绪"
|
||
时只能按 struct 槽位数补 `__inherited_N` 占位名 —— 偏移对、**名字丢**,
|
||
于是继承字段的属性访问找不到字段(`[fast-fail] 属性 'Next' 在类 'ParamNode'
|
||
中找不到`)。本方法把名字补回来:按声明顺序遍历父类,逐个取其声明里的字段名
|
||
(AnnAssign 目标名,见 `_EmitExternalClassDeclLlvm` 的成员收集),与 struct
|
||
头部槽位按序配对。名字来自**声明**,槽位类型仍取 struct 自身元素,二者一致。
|
||
|
||
泛型基类回退:父类名可能是特化体 `X[Arg]`(特化体不在任何 .vpi 中 ⇒ 直接
|
||
加载必然落空),此时回退用**模板名** `X` 加载其成员 —— 模板里声明了字段名
|
||
(如 `GSListNode[T]: Next: T*` 的 `Next`),这正是编译期唯一可信的名字来源。
|
||
与 [`basegen._get_or_create_struct` 的 `_struct_sha1_map` 拼名规则]、
|
||
[DeclarationGenerator._get_inherited_members 取模板成员] 同一条"类名 → 模板/模块"
|
||
规则源。
|
||
|
||
返回的列表长度可能小于 Limit(父类声明不可得 / 类内字段名重复)——调用方对
|
||
缺失位置继续用 `__inherited_N` 占位,保证偏移语义不变。
|
||
"""
|
||
Names: list[str] = []
|
||
Seen: set[str] = set()
|
||
for _Pc in ParentClasses:
|
||
if not _Pc:
|
||
continue
|
||
_cands: list[str] = [_Pc]
|
||
_tpl: str = _Pc.split('[', 1)[0]
|
||
if _tpl and _tpl != _Pc:
|
||
_cands.append(_tpl)
|
||
_members: list = []
|
||
for _key in _cands:
|
||
_m_cur: list = Gen.class_members.get(_key) or []
|
||
if _m_cur and not any(n.startswith('__inherited_') for n, _ in _m_cur):
|
||
_members = _m_cur
|
||
break
|
||
_sha: str | None = getattr(Gen, 'class_sha1_map', {}).get(_key)
|
||
_loaded: bool = False
|
||
if _sha:
|
||
_loaded = self._TryLoadClassMembersFromPyi(_key, f"{_sha}.stub.ll", Gen)
|
||
else:
|
||
for _mod_name, _mod_sha1 in (getattr(Gen, 'ModuleSha1Map', {}) or {}).items():
|
||
if self._TryLoadClassMembersFromPyi(_key, f"{_mod_sha1}.stub.ll", Gen):
|
||
_loaded = True
|
||
break
|
||
_m_new: list = Gen.class_members.get(_key) or []
|
||
if _loaded and _m_new and not any(n.startswith('__inherited_') for n, _ in _m_new):
|
||
_members = _m_new
|
||
break
|
||
for _nm, _ in _members:
|
||
if _nm.startswith('__inherited_') or _nm == '__vtable__':
|
||
continue
|
||
if _nm not in Seen:
|
||
Seen.add(_nm)
|
||
Names.append(_nm)
|
||
if len(Names) >= Limit:
|
||
break
|
||
return Names
|
||
|
||
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin,
|
||
force: bool = False) -> bool:
|
||
"""从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。
|
||
|
||
force=True 时(跨模块展平父类的强制重载):先清空该类的成员数据再重建,
|
||
保证并行 worker 间展平结果确定性一致(缺啥补啥、字段顺序与定义模块一致),
|
||
避免此前"部分成员被冻结、guard 阻止重载"导致的 class_members[Generator]
|
||
83~106 vs 114 不确定变体(this_adjust 错位崩溃)。
|
||
"""
|
||
ProjectRoot = self._find_project_root()
|
||
temp_dir = os.path.join(ProjectRoot, 'temp')
|
||
# guard(2026-xx 收紧):只在"表非空 **且已登记为已展平**"时短路。原实现
|
||
# 只看 `len>0`,于是"停在自有字段"的残缺表(展平未完成)一旦生成就再也不能
|
||
# 补齐(顺序敏感)——这正是自有字段下标缺继承前缀的成因。未展平的表允许
|
||
# 重跑 `_EmitExternalClassDeclLlvm`(其自有字段收集按名去重、继承前缀按名
|
||
# 去重,重跑幂等),从而与父类/struct 的就绪顺序无关。
|
||
# 【重入短路 · 与成员表长度解耦】该类**正在**被 `_EmitExternalClassDeclLlvm`
|
||
# 收集字段(`_collecting_members_set` 置位于本文件 :2054、解除于 :2481)时,
|
||
# 本次调用必是递归重入,直接返回、交由最外层收尾。
|
||
# 为什么判据不能带 `len(class_members)>0`:类内**第一个**字段的注解解析就会
|
||
# 重入本方法 —— CollectAnnAssignMember → ResolveAnnotationType → 泛型特化
|
||
# (list[T] 等)→ _EmitClassLlvm → StructGen._generate_structs
|
||
# → _TryLoadStructFromStub(X) → 本方法(X)。此刻 class_members[X] 仍为空
|
||
# (第一个字段尚未收集完),旧判据(要求 len>0)放行 ⇒ 无限递归(每轮 4 帧):
|
||
# _TryLoadClassMembersFromPyi(X) → _EmitExternalClassDeclLlvm(X)
|
||
# → _TryLoadStructFromStub(X) → _TryLoadClassMembersFromPyi(X) …
|
||
# 现场(2026-09-24 深度探针):match.MatchOr 的 patterns、stmts.Interactive 的
|
||
# body 等"首字段即指针/容器"的类;RecursionError 被上层 except 吞掉后表现为
|
||
# `WARN: … 忽略异常 maximum recursion depth exceeded` + 类型解析静默退化
|
||
# (失败的 typedef 报文、注解赋值 RHS 求值为空等次生现象)。
|
||
_collecting: set | None = getattr(Gen, '_collecting_members_set', None)
|
||
if ((not force) and (
|
||
(_collecting is not None and class_name in _collecting)
|
||
or (class_name in Gen.class_members
|
||
and len(Gen.class_members[class_name]) > 0
|
||
and class_name in Gen._members_flattened))):
|
||
# guard 命中:class_members 已存在(可能从别处预置、不完整)或该类正在
|
||
# 收集中;但 class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names
|
||
# 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114)
|
||
# → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载
|
||
# class_members,避免覆盖可能更完整的已有数据)。
|
||
_path0: str = os.path.join(temp_dir, stub_filename.replace('.stub.ll', '.vpi'))
|
||
try:
|
||
_tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0)
|
||
if _tree0 is None and os.path.isfile(_path0):
|
||
with open(_path0, 'r', encoding='utf-8') as _fh0:
|
||
_tree0 = ast.parse(_fh0.read(), _path0)
|
||
getattr(self, '_pyi_cache', {})[_path0] = _tree0
|
||
if _tree0 is not None:
|
||
for _n0 in _tree0.body:
|
||
if isinstance(_n0, ast.ClassDef) and _n0.name == class_name:
|
||
_b0: list[str] = []
|
||
for _bb in _n0.bases:
|
||
_bn0: str | None = getattr(_bb, 'id', None) or getattr(_bb, 'attr', None)
|
||
if _bn0 and _bn0 not in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'):
|
||
_b0.append(_bn0)
|
||
if _b0:
|
||
if not hasattr(Gen, 'class_parents'):
|
||
Gen.class_parents = {}
|
||
Gen.class_parents[class_name] = _b0
|
||
break
|
||
except Exception as _e0:
|
||
# 同族口径:此处原为 `except Exception: pass`(静默)。class_parents
|
||
# 未回填 ⇒ 多继承/菱形展平不完整 ⇒ class_members 长度不确定 ⇒
|
||
# this_adjust 错位。改为显式诊断(strict fail-fast / 非严格告警)。
|
||
_ReportDeclarationParseFailure(
|
||
_path0, _e0,
|
||
f"类 '{class_name}' 的 class_parents 未能回填,"
|
||
"多继承展平可能不完整(字段下标/this_adjust 可能错位)")
|
||
return False
|
||
pyi_basename = stub_filename.replace('.stub.ll', '.vpi')
|
||
pyi_path = os.path.join(temp_dir, pyi_basename)
|
||
# 已探测过的路径(_pyi_cache 含键,值为树或 None=不存在)不再重复
|
||
# os.path.isfile(Windows stat ~0.5ms/次;本函数 28543 次调用 × 探测
|
||
# ≈ 12.8s,占 _path_isfile 总耗时 63%)。
|
||
if pyi_path not in self._pyi_cache and not os.path.isfile(pyi_path):
|
||
# 回退读取旧缓存 .pyi
|
||
pyi_basename = stub_filename.replace('.stub.ll', '.pyi')
|
||
pyi_path = os.path.join(temp_dir, pyi_basename)
|
||
|
||
if self._pyi_cache_dir != temp_dir:
|
||
self._pyi_cache.clear()
|
||
self._pyi_cache_dir = temp_dir
|
||
|
||
if pyi_path not in self._pyi_cache:
|
||
if not os.path.isfile(pyi_path):
|
||
self._pyi_cache[pyi_path] = None
|
||
return False
|
||
try:
|
||
with open(pyi_path, 'r', encoding='utf-8') as f:
|
||
self._pyi_cache[pyi_path] = ast.parse(f.read(), pyi_path)
|
||
except Exception as e:
|
||
# 同族口径:原告警不带文件名("忽略异常"),改为显式诊断
|
||
# (strict fail-fast / 非严格告警)。.vpi 是编译器无糖产物,
|
||
# 解析失败属内部不变量被破坏,非严格模式仍缓存 None 并返回 False。
|
||
_ReportDeclarationParseFailure(
|
||
pyi_path, e,
|
||
f"类 '{class_name}' 的成员/父类信息不会加载,"
|
||
"class_members 可能残缺(字段下标/this_adjust 可能错位)")
|
||
self._pyi_cache[pyi_path] = None
|
||
return False
|
||
|
||
pyi_tree = self._pyi_cache[pyi_path]
|
||
if pyi_tree is None:
|
||
return False
|
||
source_sha1 = stub_filename.replace('.stub.ll', '')
|
||
module_name = None
|
||
if hasattr(Gen, 'ModuleSha1Map'):
|
||
for mod_name, mod_sha1 in Gen.ModuleSha1Map.items():
|
||
if mod_sha1 == source_sha1 and '.' not in mod_name:
|
||
module_name = mod_name
|
||
break
|
||
if module_name is None:
|
||
for mod_name, mod_sha1 in Gen.ModuleSha1Map.items():
|
||
if mod_sha1 == source_sha1:
|
||
module_name = mod_name
|
||
break
|
||
if module_name is None and hasattr(Gen, 'module_sha1') and Gen.module_sha1 == source_sha1:
|
||
module_name = '__self__'
|
||
# module_name 反查失败时(当前模块未直接导入定义模块),
|
||
# 仍需注册类成员和 vtable 信息。传 source_sha1 让 _EmitExternalClassDeclLlvm
|
||
# 正确设置 class_sha1_map 和注册 vtable。
|
||
# 类级 pyi 树节点索引:同一 (source_sha1, class_name) 的 ClassDef 节点在进程内
|
||
# 唯一(_pyi_cache 以 pyi_path 为键只 parse 一次),避免跨 worker 任务反复
|
||
# 线性扫描 pyi_tree.body 查找同一定义类。
|
||
found = False
|
||
node = self._extern_class_decl_cache.get((source_sha1, class_name))
|
||
if node is not None:
|
||
found = True
|
||
else:
|
||
for node in pyi_tree.body:
|
||
if isinstance(node, ast.ClassDef) and node.name == class_name:
|
||
self._extern_class_decl_cache[(source_sha1, class_name)] = node
|
||
found = True
|
||
break
|
||
if not found:
|
||
return False
|
||
if force:
|
||
# 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。
|
||
# 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。
|
||
Gen.class_members[class_name] = []
|
||
# 同步撤销"已展平"登记:重建若因依赖未就绪而不完整,须允许后续查询再补齐。
|
||
Gen._members_flattened.discard(class_name)
|
||
for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields',
|
||
'class_member_byteorders', 'class_member_bitoffsets'):
|
||
_gd: dict | None = getattr(Gen, _gd_attr, None)
|
||
if _gd is not None:
|
||
_gd.pop(class_name, None)
|
||
if hasattr(Gen, 'class_parents'):
|
||
Gen.class_parents.pop(class_name, None)
|
||
if hasattr(Gen, 'class_parent'):
|
||
Gen.class_parent.pop(class_name, None)
|
||
# 去重登记(force=True 时不登记,见下);在 try 之外先绑定,
|
||
# 便于发射失败时统一撤销登记。
|
||
_sigmap: dict | None = None
|
||
_sig_key: tuple | None = None
|
||
_sig: tuple | None = None
|
||
if not force:
|
||
# 【稳定态去重 · 依赖签名】guard(上方)只在"表非空 **且已登记已展平**"
|
||
# 时短路;而"父类是泛型特化/始终不可得"的类**永远不会**被登记为已展平
|
||
# ⇒ guard 永不命中 ⇒ 每次重入都重跑整套 `_EmitExternalClassDeclLlvm`
|
||
# (其内部又触发 _generate_structs → _TryLoadStructFromStub → 本方法,
|
||
# 形成每轮 4 帧的环)。profile(2026-09-29 main_prof_72472)实测:
|
||
# _TryLoadClassMembersFromPyi 142,888 次 → 6,683 次落到 _EmitExternalClassDeclLlvm
|
||
# (cum 103.1s)→ _generate_structs 8,059 次(cum 128.7s)
|
||
# → _TryLoadStructFromStub 88,970 次 → _parse_llvm_type 909,382 次
|
||
# 即"环上重复重跑"而非"首次发射"是主开销。
|
||
# 依赖签名(与 EnsureClassMembersFlattened 的"(struct 元素数, 成员表长度)
|
||
# 签名"同一思路):签名不变 ⇒ 上一次发射已经把该状态下能填的都填完了,
|
||
# 重跑是纯 no-op,直接短路。**只在签名变化时才允许重跑** ⇒ 与"父类/struct
|
||
# 就绪顺序无关"这条设计目标不冲突(依赖一变,签名就变,立刻放行)。
|
||
# 保守量:全局"已登记类数 / 已展平类数"——声明加载只要有任何进展就会变,
|
||
# 因此不会把"别的类补齐后本类该重跑"的情形漏掉。
|
||
# 键含 source_sha1:同名类可能分属不同模块,发射是 per-(sha1, 类名) 的。
|
||
_sigmap: dict | None = getattr(Gen, '_class_members_reload_sig', None)
|
||
if _sigmap is None:
|
||
_sigmap = {}
|
||
Gen._class_members_reload_sig = _sigmap
|
||
_sig_key: tuple = (source_sha1, class_name)
|
||
_sig: tuple = self._ClassMembersReloadSignature(Gen, class_name)
|
||
if _sigmap.get(_sig_key) == _sig:
|
||
return True
|
||
# 先登记、再发射:**登记必须在发射之前**,否则环上第一个内层重入
|
||
# (发射进行中,状态已变)仍会看到"无签名"而再发一次,去重被推迟一层;
|
||
# 先登记后内层重入即可在同状态下直接短路(环深 2 层即收敛)。
|
||
_sigmap[_sig_key] = _sig
|
||
try:
|
||
self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name, source_sha1=source_sha1)
|
||
except BaseException:
|
||
# 发射失败(fail-fast 或将被上层 except 吞掉):撤销本次签名登记,
|
||
# 保证"失败可重试"这一既有语义不被去重挡死——否则失败态会被签名
|
||
# 永久冻结(正是本仓反复踩过的"残缺表冻结"类缺陷)。
|
||
if _sigmap is not None and _sigmap.get(_sig_key) == _sig:
|
||
del _sigmap[_sig_key]
|
||
raise
|
||
return True
|
||
|
||
def _ClassMembersReloadSignature(self, Gen: LlvmGeneratorMixin, class_name: str) -> tuple:
|
||
"""`_TryLoadClassMembersFromPyi` 重跑的依赖签名(纯读,不产生副作用)。
|
||
|
||
收录"重跑能否产生新结果"的全部输入:
|
||
- 全局进度:**逐类**(`class_members` 键, 该表长度) 的完整元组(不是"类数"——
|
||
只看键数会在"某已有类的成员表继续增长"(父类成员后到)时看不出变化,
|
||
而父类成员正是继承前缀的来源);`_members_flattened` 长度;
|
||
`class_parents` / `class_parent` 的条目数(父链登记本身也是输入)
|
||
- 本类自身:成员表长度、是否已登记已展平、struct body 元素数(None ⇒ -1)
|
||
- 各父类:成员表长度 + 是否已展平(父类从 `class_parents` / `class_parent` 取;
|
||
两者都缺时用空元组 —— 此时本类依赖尚未建立,签名仍稳定,不影响收敛)
|
||
"""
|
||
_parents: list = list((getattr(Gen, 'class_parents', {}) or {}).get(class_name) or [])
|
||
if not _parents:
|
||
_p1: str | None = (getattr(Gen, 'class_parent', {}) or {}).get(class_name)
|
||
if _p1:
|
||
_parents = [_p1]
|
||
_flat: set = getattr(Gen, '_members_flattened', None) or set()
|
||
_st: object = Gen.structs.get(class_name)
|
||
_st_len: int = -1
|
||
if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None:
|
||
_st_len = len(_st.elements)
|
||
_parent_sig: tuple = tuple(
|
||
(_p, len(Gen.class_members.get(_p, []) or []), _p in _flat)
|
||
for _p in _parents
|
||
)
|
||
_cm_sig: tuple = tuple((_k, len(_v)) for _k, _v in Gen.class_members.items())
|
||
return (
|
||
_cm_sig,
|
||
len(_flat),
|
||
len(getattr(Gen, 'class_parents', {}) or {}),
|
||
len(getattr(Gen, 'class_parent', {}) or {}),
|
||
len(Gen.class_members.get(class_name, []) or []),
|
||
class_name in _flat,
|
||
_st_len,
|
||
_parent_sig,
|
||
)
|
||
|
||
def _BuildScalarConstant(self, value_node: ast.AST, target_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Constant | None:
|
||
"""Build a scalar constant value from AST node, delegated to unified method."""
|
||
return self.Trans._BuildScalarConstantUnified(value_node, target_type, Gen)
|
||
|
||
@staticmethod
|
||
def _CountStubFilesInDir(DirPath: str) -> int:
|
||
"""目录下 `*.stub.ll` 的数量(同一「目录 + 目录 mtime」只 listdir 一次)。
|
||
|
||
为什么可缓存:stub 目录**只增不改**,且由主进程在批次之间写入 ⇒ 只有目录
|
||
条目增删才会改变计数,而 Windows/NTFS 在条目增/删/改名时会更新目录 mtime。
|
||
命中缓存即复用上次计数,省掉 `os.listdir` + 全量 `endswith('.stub.ll')`
|
||
判定 —— 这两个统计只为在 `_LookupStubFuncType` 里构造"是否新增 stub"的
|
||
签名,而该函数位于每次跨模块函数/方法查找的热路径上(20 个调用点),
|
||
工程越大(stub 越多)这份与编译工作量无关的开销越高。
|
||
|
||
取不到 stat(目录不存在 / 权限)时**不写缓存**,退化为每次实算:
|
||
计数仍是权威判据,缓存只决定"要不要重新 listdir",不改变任何判定结果。
|
||
"""
|
||
if not os.path.isdir(DirPath):
|
||
return 0
|
||
try:
|
||
_Mtime: int = os.stat(DirPath).st_mtime_ns
|
||
except OSError:
|
||
return sum(1 for _f in os.listdir(DirPath) if _f.endswith('.stub.ll'))
|
||
_Hit: tuple[int, int] | None = ImportHandle._stub_dir_count_cache.get(DirPath)
|
||
if _Hit is not None and _Hit[0] == _Mtime:
|
||
return _Hit[1]
|
||
_Count: int = sum(1 for _f in os.listdir(DirPath) if _f.endswith('.stub.ll'))
|
||
ImportHandle._stub_dir_count_cache[DirPath] = (_Mtime, _Count)
|
||
return _Count
|
||
|
||
def _LookupStubFuncType(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None:
|
||
"""从 stub.ll 文件中查找指定函数的 LLVM 类型
|
||
|
||
扫描 output/ 和 temp/ 两个目录的 .stub.ll 文件。
|
||
关键修复:output/(Phase 2 完整结果)必须覆盖 temp/(Phase 1 不完整结果),
|
||
否则继承包装方法(在 Phase 2 的 _generate_inherited_method_wrappers 中生成)
|
||
将无法被解析,导致跨模块调用使用默认 i32 返回类型 → 64 位指针截断。
|
||
"""
|
||
|
||
ProjectRoot = self._find_project_root()
|
||
temp_dir = os.path.join(ProjectRoot, 'temp')
|
||
output_dir = os.path.join(ProjectRoot, 'output')
|
||
|
||
if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir):
|
||
return None
|
||
|
||
# 文件数签名:检测 stub 新增(增量编译时 output 逐步填充完整签名)。
|
||
# 类级缓存(ImportHandle._stub_func_cache)跨 worker 任务共享;签名变化
|
||
# 只增量解析新增 stub 文件(编译进程内 stub 只增不改,旧文件不重扫,
|
||
# 省略每文件 os.path.isfile ——Windows stat ~0.5ms/次,原 5840 次 ≈ 6.5s)。
|
||
# 覆盖语义不变:先扫 temp/ 后扫 output/,output 同名声明覆盖 temp。
|
||
# 计数本身也不重复 listdir:走 _CountStubFilesInDir 的「目录 mtime 命中即复用」
|
||
# 缓存(原实现每次调用都 listdir + 全量 endswith 判定,纯浪费)。
|
||
output_count = self._CountStubFilesInDir(output_dir)
|
||
temp_count = self._CountStubFilesInDir(temp_dir)
|
||
signature = (output_count, temp_count)
|
||
if ImportHandle._stub_func_cache_signature != signature:
|
||
ImportHandle._stub_func_cache_signature = signature
|
||
for scan_dir in [temp_dir, output_dir]:
|
||
if not os.path.isdir(scan_dir):
|
||
continue
|
||
for filename in os.listdir(scan_dir):
|
||
if not filename.endswith('.stub.ll'):
|
||
continue
|
||
|
||
stub_path = os.path.join(scan_dir, filename)
|
||
if stub_path in ImportHandle._stub_func_scanned:
|
||
continue
|
||
ImportHandle._stub_func_scanned.add(stub_path)
|
||
source_sha1 = filename.replace('.stub.ll', '')
|
||
try:
|
||
with open(stub_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
for line in content.splitlines():
|
||
stripped = line.strip()
|
||
if not stripped.startswith('declare '):
|
||
continue
|
||
|
||
name_match = stripped.split('@', 1)
|
||
if len(name_match) < 2:
|
||
continue
|
||
|
||
stub_func_name = name_match[1].split('(', 1)[0].strip().strip('"')
|
||
|
||
match = re.match(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$', stripped)
|
||
if match:
|
||
stub_ret = match.group(1).strip()
|
||
stub_params = match.group(3).strip()
|
||
ImportHandle._stub_func_cache[stub_func_name] = (stub_ret, stub_params, '...' in stub_params, source_sha1)
|
||
except FileNotFoundError:
|
||
# listdir 与 open 之间文件被并发删除:跳过(等效旧 isfile 预检查)
|
||
continue
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
|
||
if func_name not in self._stub_func_cache:
|
||
return None
|
||
|
||
ret_str, params_str, is_variadic, source_sha1 = self._stub_func_cache[func_name]
|
||
ret_type = self._parse_llvm_type(ret_str, Gen, source_sha1=source_sha1, create_structs=False)
|
||
if ret_type is None:
|
||
ret_type = ir.IntType(32)
|
||
param_types = []
|
||
if params_str and params_str != 'void':
|
||
depth = 0
|
||
current = ''
|
||
for ch in params_str:
|
||
if ch == ',' and depth == 0:
|
||
p = self._strip_llvm_param_name(current.strip())
|
||
if p and p != '...':
|
||
pt = self._parse_llvm_type(p, Gen, source_sha1=source_sha1, create_structs=False)
|
||
if pt is not None:
|
||
param_types.append(pt)
|
||
else:
|
||
param_types.append(ir.PointerType(ir.IntType(8)))
|
||
current = ''
|
||
else:
|
||
if ch == '{':
|
||
depth += 1
|
||
elif ch == '}':
|
||
depth -= 1
|
||
current += ch
|
||
p = self._strip_llvm_param_name(current.strip())
|
||
if p and p != '...':
|
||
pt = self._parse_llvm_type(p, Gen, source_sha1=source_sha1, create_structs=False)
|
||
if pt is not None:
|
||
param_types.append(pt)
|
||
else:
|
||
param_types.append(ir.PointerType(ir.IntType(8)))
|
||
return ir.FunctionType(ret_type, param_types, var_arg=is_variadic)
|
||
|
||
def _strip_llvm_param_name(self, param_str: str) -> str:
|
||
"""剥离 LLVM IR 参数字符串中的参数名,仅保留类型部分"""
|
||
param_str = param_str.strip()
|
||
# 支持 %name 和 %"quoted.name" 两种参数名形式
|
||
result = re.sub(r'\s+%("[^"]*"|[\w.]+)\s*$', '', param_str)
|
||
return result.strip()
|
||
|
||
def _parse_simple_llvm_type(self, type_str: str) -> ir.Type | None:
|
||
"""解析简单的 LLVM 类型字符串(不创建结构体,避免副作用)"""
|
||
|
||
type_str = type_str.strip()
|
||
|
||
type_map = {
|
||
'void': ir.VoidType(),
|
||
'i1': ir.IntType(1),
|
||
'i8': ir.IntType(8),
|
||
'i16': ir.IntType(16),
|
||
'i32': ir.IntType(32),
|
||
'i64': ir.IntType(64),
|
||
'float': ir.FloatType(),
|
||
'double': ir.DoubleType(),
|
||
}
|
||
|
||
if type_str in type_map:
|
||
return type_map[type_str]
|
||
|
||
if type_str.endswith('*'):
|
||
pointee = type_str[:-1].strip()
|
||
pointee_type = self._parse_simple_llvm_type(pointee)
|
||
if pointee_type:
|
||
return ir.PointerType(pointee_type)
|
||
return ir.PointerType(ir.IntType(8))
|
||
|
||
if type_str.startswith('[') and ']' in type_str:
|
||
arr_match = re.match(r'\[(\d+)\s*x\s+(.+)\]', type_str)
|
||
if arr_match:
|
||
size = int(arr_match.group(1))
|
||
elem_type = self._parse_simple_llvm_type(arr_match.group(2).strip())
|
||
if elem_type:
|
||
return ir.ArrayType(elem_type, size)
|
||
|
||
if type_str.startswith('%'):
|
||
return ir.PointerType(ir.IntType(8))
|
||
|
||
return None
|
||
|
||
def _LoadDeclarationsFromStubLlvm(self, module_name: str, Gen: LlvmGeneratorMixin) -> None:
|
||
"""从 stub.ll 文件加载函数声明和结构体定义到 Gen"""
|
||
ProjectRoot = self._find_project_root()
|
||
temp_dir = os.path.join(ProjectRoot, 'temp')
|
||
|
||
if not os.path.isdir(temp_dir):
|
||
return
|
||
|
||
all_stub_files = [f for f in os.listdir(temp_dir) if f.endswith('.stub.ll')]
|
||
|
||
source_sig_files = getattr(self.Trans, '_source_module_sig_files', {})
|
||
target_sha1 = None
|
||
if module_name:
|
||
for key, val in source_sig_files.items():
|
||
if key == module_name or key.endswith('.' + module_name):
|
||
target_sha1 = os.path.basename(val).replace('.vpi', '').replace('.pyi', '')
|
||
break
|
||
if not target_sha1:
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
target_sha1 = ModuleSha1Map.get(module_name)
|
||
|
||
if target_sha1:
|
||
target_stub = f"{target_sha1}.stub.ll"
|
||
if target_stub in all_stub_files:
|
||
stub_files = [target_stub]
|
||
else:
|
||
stub_files = []
|
||
else:
|
||
stub_files = []
|
||
|
||
needed_sha1s = set()
|
||
for filename in stub_files:
|
||
stub_path = os.path.join(temp_dir, filename)
|
||
try:
|
||
with open(stub_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
for line in content.splitlines():
|
||
stripped = line.strip()
|
||
if stripped.startswith('%') and '=' in stripped and 'type' in stripped:
|
||
for m in re.finditer(r'%\"?([a-f0-9]{16})\.', stripped):
|
||
needed_sha1s.add(m.group(1))
|
||
elif stripped.startswith('declare '):
|
||
for m in re.finditer(r'%\"?([a-f0-9]{16})\.', stripped):
|
||
needed_sha1s.add(m.group(1))
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
|
||
preLoad_files = []
|
||
for sha1 in needed_sha1s:
|
||
dep_stub = f"{sha1}.stub.ll"
|
||
if dep_stub in all_stub_files and dep_stub not in stub_files:
|
||
preLoad_files.append(dep_stub)
|
||
|
||
Load_order = preLoad_files + stub_files
|
||
|
||
for filename in Load_order:
|
||
stub_path = os.path.join(temp_dir, filename)
|
||
source_sha1 = filename.replace('.stub.ll', '')
|
||
is_preLoad = filename in preLoad_files
|
||
try:
|
||
with open(stub_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
for line in content.splitlines():
|
||
stripped = line.strip()
|
||
|
||
if stripped.startswith('%') and '=' in stripped and 'type' in stripped:
|
||
struct_def_match = re.match(r'%"?((?:[a-f0-9]+\.)?([\w\[\]]+))"?\s*=\s*type\s*(.*)', stripped)
|
||
if struct_def_match:
|
||
raw_name = struct_def_match.group(1)
|
||
# 治本修复:从 raw_name 提取类型定义的实际 sha1 前缀,
|
||
# 而非使用 stub 文件的 sha1。同一 stub 文件可能包含多个模块的
|
||
# struct 定义(前向引用),必须用类型名中的 sha1 作为 source_sha1,
|
||
# 否则同名类冲突检测会失效(如 __module.py stub 中的
|
||
# e64295c03b564a53.Module 前向引用会被错误地用 6e3f70566907c6cf 作为 sha1)。
|
||
actual_type_sha1 = source_sha1
|
||
if '.' in raw_name:
|
||
_rn_parts = raw_name.split('.', 1)
|
||
if len(_rn_parts[0]) == 16 and all(_c in '0123456789abcdef' for _c in _rn_parts[0]):
|
||
actual_type_sha1 = _rn_parts[0]
|
||
clean_name = _rn_parts[1]
|
||
else:
|
||
clean_name = raw_name
|
||
struct_body = struct_def_match.group(3).strip()
|
||
if struct_body and struct_body != 'opaque':
|
||
elem_types = []
|
||
is_packed = struct_body is not None and struct_body.startswith('<{') and struct_body.endswith('}>')
|
||
if is_packed:
|
||
inner = struct_body[2:-1].strip()
|
||
else:
|
||
inner = struct_body.strip('{}')
|
||
if inner:
|
||
for elem in inner.split(','):
|
||
elem = elem.strip()
|
||
if elem:
|
||
elem_type = self._parse_llvm_type(elem, Gen, source_sha1=actual_type_sha1)
|
||
if elem_type is not None:
|
||
elem_types.append(elem_type)
|
||
else:
|
||
elem_types.append(ir.IntType(32))
|
||
st = Gen._get_or_create_struct(clean_name, source_sha1=actual_type_sha1, packed=is_packed)
|
||
if isinstance(st, ir.IdentifiedStructType) and st.elements is None:
|
||
try:
|
||
st.set_body(*elem_types)
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
continue
|
||
|
||
# 全局变量声明:@name = external global TYPE(stub.ll 实际写入格式,
|
||
# 跨模块全局变量为裸名,如 @_sha1_store_arr;原正则只匹配
|
||
# @sha1.name 前缀格式,实际从未命中 → TPC 编译 TPV 时跨模块
|
||
# 全局变量(如 StubMerger._sha1_store_count)从未嵌入
|
||
# Gen.module.globals → _lookup_module_global 恒 None → store/load
|
||
# 全部丢失 → memset(null) 崩溃。此处兼容裸名 + sha1 前缀两种格式,
|
||
# 名称与定义一致(temp/*.text.ll 均为裸名)。
|
||
_gv_match = re.match(r'@("?)(?:([a-f0-9]{16})\.)?([\w.\[\]]+)\1\s*=\s*external\s+global\s+(\S.*)', stripped)
|
||
if _gv_match:
|
||
_gv_name: str = _gv_match.group(3)
|
||
_gv_sha1: str | None = _gv_match.group(2)
|
||
_gv_type_str: str = _gv_match.group(4).strip()
|
||
# 去掉尾部修饰(zeroinitializer / , align N),只保留类型
|
||
_gv_type_str = re.sub(r'\s+zeroinitializer.*$', '', _gv_type_str)
|
||
_gv_type_str = re.sub(r'\s*,\s*align\s+\d+.*$', '', _gv_type_str)
|
||
if _gv_name not in Gen.module.globals:
|
||
_gv_ty: ir.Type | None = self._parse_llvm_type(_gv_type_str, Gen, source_sha1=source_sha1)
|
||
if _gv_ty is not None:
|
||
_gv = ir.GlobalVariable(Gen.module, _gv_ty, name=_gv_name)
|
||
_gv.linkage = 'external'
|
||
continue
|
||
|
||
if not stripped.startswith('declare '):
|
||
continue
|
||
|
||
if is_preLoad:
|
||
continue
|
||
|
||
func_name_match = stripped.split('@', 1)
|
||
if len(func_name_match) < 2:
|
||
continue
|
||
|
||
func_name = func_name_match[1].split('(', 1)[0].strip().strip('"')
|
||
|
||
if not module_name:
|
||
continue
|
||
|
||
# bare 名(无 16 位 hex SHA1 前缀)表示 CExport,调用点需用纯名
|
||
_Prefix: str = func_name.split('.', 1)[0]
|
||
if not (len(_Prefix) == 16 and all(_c in '0123456789abcdef' for _c in _Prefix)):
|
||
Gen._export_funcs.add(func_name)
|
||
|
||
try:
|
||
func_type = self._parse_llvm_declare(stripped, Gen)
|
||
if func_type is not None:
|
||
# __new__ 方法应返回类指针类型(堆分配替换),与 _GetFunctionSignatureLlvm/_EmitFunctionLlvm 保持一致
|
||
# .stub.ll 由 .pyi 生成,.pyi 生成器对无返回类型注解的 __new__ 默认使用 t.CInt,这里修正为 ClassName*
|
||
if '.__new__' in func_name:
|
||
_np = func_name.split('.')
|
||
if len(_np[0]) == 16 and all(_c in '0123456789abcdef' for _c in _np[0]):
|
||
_new_class = _np[1]
|
||
else:
|
||
_new_class = _np[0]
|
||
_new_struct = Gen.structs.get(_new_class)
|
||
if _new_struct is None:
|
||
_full_name = func_name.split('.__new__')[0]
|
||
_new_struct = Gen.structs.get(_full_name)
|
||
if _new_struct:
|
||
_ret_type = _new_struct if isinstance(_new_struct, ir.PointerType) else ir.PointerType(_new_struct)
|
||
func_type = ir.FunctionType(_ret_type, func_type.args, var_arg=func_type.var_arg)
|
||
if func_name in Gen.functions:
|
||
existing_func = Gen.functions[func_name]
|
||
existing_ftype = getattr(existing_func, 'ftype', None) or existing_func.type.pointee
|
||
if str(existing_ftype) != str(func_type):
|
||
# 类型不一致:清理 module.globals + module.scope._useset 后重建,
|
||
# 避免 DuplicatedNameError。必须同时清 existing_func.name(真名)
|
||
# 与 func_name(字典键):别名场景下两者不同(如
|
||
# Gen.functions['ExprGenAttr.find_global_in_module'] 指向名为
|
||
# 'dddc595175f00c66.ExprGenAttr.find_global_in_module' 的对象),
|
||
# 只删真名会留下字典键仍占住 module.scope(llvmlite 按名字去重)
|
||
# → 重建仍抛 DuplicatedNameError(报文只有名字,不含冲突双方)。
|
||
module_scope = getattr(Gen.module, 'scope', None)
|
||
_useset = getattr(module_scope, '_useset', None)
|
||
for _stale_name in {existing_func.name, func_name}:
|
||
_stale = Gen.module.globals.get(_stale_name)
|
||
if isinstance(_stale, ir.Function):
|
||
del Gen.module.globals[_stale_name]
|
||
if _useset is not None:
|
||
_useset.discard(_stale_name)
|
||
func = Gen._CreateOrReuseFuncDecl(func_name, func_type)
|
||
Gen.functions[func_name] = func
|
||
if '.' in func_name:
|
||
short_name = func_name.split('.', 1)[1]
|
||
Gen.functions[short_name] = func
|
||
else:
|
||
func = Gen._get_or_declare_function(func_name, func_type)
|
||
Gen.functions[func_name] = func
|
||
if '.' in func_name:
|
||
short_name = func_name.split('.', 1)[1]
|
||
Gen.functions[short_name] = func
|
||
except Exception as _e:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
if isinstance(_e, _FFE):
|
||
# 声明创建/签名的 fail-fast 必须上抛(见 _LoadModuleDeclarationsFromFile
|
||
# 同款说明):吞掉它会让该声明静默缺失、并把半注册名字留给下一轮。
|
||
raise
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
except Exception as _e:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
if isinstance(_e, _FFE):
|
||
raise
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
|
||
def _TryLoadFuncDeclsFromOutputStub(self, struct_name: str, Gen: LlvmGeneratorMixin) -> bool:
|
||
"""按需从 output 目录的 stub.ll 加载泛型特化方法声明。
|
||
|
||
当 temp stub 不包含泛型特化(如 list[str].__iter__)时,
|
||
从 output stub(由 _split_ll 从完整 IR 生成)中按需加载。
|
||
|
||
struct_name: 结构体名,如 '9163064cf3eb88f4.list[str]' 或 'list[str]'
|
||
返回 True 如果找到了并加载了新函数声明。
|
||
"""
|
||
ProjectRoot = self._find_project_root()
|
||
output_dir = os.path.join(ProjectRoot, 'output')
|
||
if not os.path.isdir(output_dir):
|
||
return False
|
||
|
||
# 提取短名称和 SHA1 前缀
|
||
sha1_prefix: str | None = None
|
||
if '.' in struct_name:
|
||
parts: list[str] = struct_name.split('.', 1)
|
||
if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]):
|
||
sha1_prefix = parts[0]
|
||
short_name: str = parts[1]
|
||
else:
|
||
short_name = struct_name
|
||
else:
|
||
short_name = struct_name
|
||
|
||
# 确定要搜索的 stub 文件列表
|
||
stub_files_to_search: list[str] = []
|
||
if sha1_prefix:
|
||
target_stub = f'{sha1_prefix}.stub.ll'
|
||
target_path = os.path.join(output_dir, target_stub)
|
||
if os.path.isfile(target_path):
|
||
stub_files_to_search.append(target_stub)
|
||
else:
|
||
# 没有SHA1前缀,搜索所有 output stub
|
||
for fname in os.listdir(output_dir):
|
||
if fname.endswith('.stub.ll'):
|
||
stub_files_to_search.append(fname)
|
||
|
||
loaded_any: bool = False
|
||
for fname in stub_files_to_search:
|
||
stub_path = os.path.join(output_dir, fname)
|
||
try:
|
||
with open(stub_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
for line in content.splitlines():
|
||
stripped = line.strip()
|
||
if not stripped.startswith('declare '):
|
||
continue
|
||
# 检查是否是目标结构体的方法
|
||
if f'.{short_name}.' not in stripped:
|
||
continue
|
||
try:
|
||
func_type = self._parse_llvm_declare(stripped, Gen)
|
||
if func_type is None:
|
||
continue
|
||
# 提取函数名:最后一个 @ 到第一个 ( 之间的内容
|
||
paren_pos: int = stripped.index('(')
|
||
at_pos: int = stripped.rindex('@', 0, paren_pos)
|
||
func_name: str = stripped[at_pos + 1:paren_pos].strip().strip('"')
|
||
if func_name not in Gen.functions:
|
||
# module.globals 已有同名声明 → 复用(llvmlite NameScope
|
||
# 按名字去重,重复 ir.Function 必抛 DuplicatedNameError;
|
||
# 此前该异常被下方 except 静默吞掉 → 声明未登记 → 后续
|
||
# 调用点找不到签名)
|
||
g_ex_stub = Gen.module.globals.get(func_name)
|
||
if g_ex_stub is not None:
|
||
func = g_ex_stub
|
||
else:
|
||
func = Gen._CreateOrReuseFuncDecl(func_name, func_type)
|
||
Gen.functions[func_name] = func
|
||
if '.' in func_name:
|
||
short_fn = func_name.split('.', 1)[1]
|
||
Gen.functions[short_fn] = func
|
||
loaded_any = True
|
||
except Exception as _e:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
if isinstance(_e, _FFE):
|
||
# 见 _LoadModuleDeclarationsFromFile 同款说明:fail-fast 必须上抛。
|
||
# 原实现是裸 `except Exception: pass` —— 创建中途失败(半注册名字)
|
||
# 会在这里被彻底吞掉,后续同名创建只能报"名字悬空占用"。
|
||
raise
|
||
_vlog().warning(
|
||
f"按需加载 output stub 声明失败: '{stripped}' → {_e}", "Exception")
|
||
except Exception as _e:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
if isinstance(_e, _FFE):
|
||
raise
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"按需加载 output stub 失败: {_e}", "Exception")
|
||
return loaded_any
|
||
|
||
def _parse_llvm_declare(self, declare_line: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None:
|
||
"""解析 LLVM declare 语句"""
|
||
match = re.match(r'declare\s+(.+?)\s+@("?[^()]+"?)\s*\((.*)\)$', declare_line)
|
||
if not match:
|
||
return None
|
||
|
||
ret_type_str = match.group(1).strip()
|
||
params_str = match.group(3).strip()
|
||
|
||
ret_type = self._parse_llvm_type(ret_type_str, Gen)
|
||
if ret_type is None:
|
||
return None
|
||
|
||
param_types = []
|
||
if params_str and params_str != 'void':
|
||
depth = 0
|
||
current = ''
|
||
for ch in params_str:
|
||
if ch == ',' and depth == 0:
|
||
p = self._strip_llvm_param_name(current.strip())
|
||
if p and p != '...':
|
||
pt = self._parse_llvm_type(p, Gen)
|
||
if pt is not None:
|
||
param_types.append(pt)
|
||
current = ''
|
||
else:
|
||
if ch == '{':
|
||
depth += 1
|
||
elif ch == '}':
|
||
depth -= 1
|
||
current += ch
|
||
p = self._strip_llvm_param_name(current.strip())
|
||
if p and p != '...':
|
||
pt = self._parse_llvm_type(p, Gen)
|
||
if pt is not None:
|
||
param_types.append(pt)
|
||
|
||
is_variadic = '...' in params_str
|
||
return ir.FunctionType(ret_type, param_types, var_arg=is_variadic)
|
||
|
||
@staticmethod
|
||
def _IsGenIndependentLlvmType(T: ir.Type) -> bool:
|
||
"""该 llvm 类型是否**不含具名结构体**(⇒ 结果与 `Gen.structs` 状态无关,可缓存)。
|
||
|
||
具名结构体(`IdentifiedStructType`)由 `Gen._get_or_create_struct` 创建、
|
||
会被 `_generate_structs` 清除重建(instance 与注册表状态绑定),且
|
||
`create_structs=False` 分支返回的 None 表示"此刻未加载"(状态依赖)
|
||
⇒ 这两类结果一律不进 memo。
|
||
"""
|
||
if isinstance(T, ir.IdentifiedStructType):
|
||
return False
|
||
if isinstance(T, ir.PointerType):
|
||
return ImportHandle._IsGenIndependentLlvmType(T.pointee)
|
||
if isinstance(T, ir.ArrayType):
|
||
return ImportHandle._IsGenIndependentLlvmType(T.element)
|
||
if isinstance(T, ir.VectorType):
|
||
return ImportHandle._IsGenIndependentLlvmType(T.element)
|
||
if isinstance(T, ir.LiteralStructType):
|
||
return all(ImportHandle._IsGenIndependentLlvmType(_e) for _e in T.elements)
|
||
if isinstance(T, ir.FunctionType):
|
||
return (ImportHandle._IsGenIndependentLlvmType(T.return_type)
|
||
and all(ImportHandle._IsGenIndependentLlvmType(_a) for _a in T.args))
|
||
return True
|
||
|
||
def _parse_llvm_type(self, type_str: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None, create_structs: bool = True) -> ir.Type | None:
|
||
"""`_parse_llvm_type_impl` 的 memo 包装(只缓存与 Gen 状态无关的结果)。
|
||
|
||
profile(2026-09-29 main_prof_72472):本函数被调 1,689,809 次
|
||
(cum 46.7s,其中 909,382 次来自 `_TryLoadStructFromStub`、57,525 次来自
|
||
`_LoadDeclarationsFromStubLlvm`、683,127 次是自递归),并且每次 `X*` 都要
|
||
新建 `ir.PointerType` —— 全程序 `_TypedPointerType.__init__` 4,596,463 次
|
||
(≈12–15s)主要来自这里。而 `i8*`/`i32`/`[N x i8]` 这些**纯字面类型串**的
|
||
解析结果与任何 Gen 状态无关,重复解析纯属浪费 ⇒ 加 memo。
|
||
"""
|
||
_key_str: str = type_str.strip()
|
||
_key: tuple = (_key_str, source_sha1, create_structs)
|
||
_memo: dict = self._llvm_type_memo
|
||
try:
|
||
return _memo[_key]
|
||
except KeyError:
|
||
pass
|
||
_res: ir.Type | None = self._parse_llvm_type_impl(
|
||
_key_str, Gen, source_sha1=source_sha1, create_structs=create_structs)
|
||
if _res is not None and ImportHandle._IsGenIndependentLlvmType(_res):
|
||
_memo[_key] = _res
|
||
return _res
|
||
|
||
def _parse_llvm_type_impl(self, type_str: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None, create_structs: bool = True) -> ir.Type | None:
|
||
"""解析 LLVM 类型字符串"""
|
||
|
||
type_str = type_str.strip()
|
||
|
||
# 模块级常量(原为每调用重建的字典字面量,见 _LLVM_PRIMITIVE_TYPE_MAP 注释)
|
||
type_map: dict[str, ir.Type] = _LLVM_PRIMITIVE_TYPE_MAP
|
||
|
||
if type_str in type_map:
|
||
return type_map[type_str]
|
||
|
||
if type_str.startswith('{') and type_str.endswith('}'):
|
||
inner = type_str[1:-1].strip()
|
||
field_types = []
|
||
depth = 0
|
||
current = ''
|
||
for ch in inner:
|
||
if ch == ',' and depth == 0:
|
||
field_types.append(current.strip())
|
||
current = ''
|
||
else:
|
||
if ch == '{':
|
||
depth += 1
|
||
elif ch == '}':
|
||
depth -= 1
|
||
current += ch
|
||
if current.strip():
|
||
field_types.append(current.strip())
|
||
llvm_fields = []
|
||
for ft in field_types:
|
||
parsed = self._parse_llvm_type(ft, Gen, source_sha1=source_sha1, create_structs=create_structs)
|
||
if parsed is not None:
|
||
llvm_fields.append(parsed)
|
||
else:
|
||
return None
|
||
if llvm_fields:
|
||
return ir.LiteralStructType(llvm_fields)
|
||
return None
|
||
|
||
if type_str.endswith('*'):
|
||
pointee = type_str[:-1].strip()
|
||
pointee_type = self._parse_llvm_type(pointee, Gen, source_sha1=source_sha1, create_structs=create_structs)
|
||
if pointee_type is not None:
|
||
return ir.PointerType(pointee_type)
|
||
# 被指类型是**具名聚合**(`%...`)却在"允许建结构体"模式下解析不出来:
|
||
# 具名类型必然可由 _get_or_create_struct 兜出,取不到即内部不变量已被破坏
|
||
# (成员表/布局两链不同源、名字带多余前缀等)。静默退化成 i8* 会把
|
||
# 「结构体指针」悄悄变成通用指针,外层再套一层即成 i8**(现场:
|
||
# Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。
|
||
# create_structs=False 时未加载不属异常 ⇒ 只在该模式下响亮失败。
|
||
if create_structs and pointee.startswith('%'):
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
raise _FFE(
|
||
f"具名类型指针的被指类型解析失败:'{type_str}' 的被指部分 '{pointee}'"
|
||
f" 在 create_structs=True 下仍取不到结构体(source_sha1={source_sha1})"
|
||
f"—— 拒绝静默退化为 i8*(会使结构体指针变通用指针,再套一层即 i8**)")
|
||
return ir.PointerType(ir.IntType(8))
|
||
|
||
if type_str.startswith('[') and ']' in type_str:
|
||
arr_match = re.match(r'\[(\d+)\s*x\s+(.+)\]', type_str)
|
||
if arr_match:
|
||
size = int(arr_match.group(1))
|
||
elem_type = self._parse_llvm_type(arr_match.group(2).strip(), Gen, source_sha1=source_sha1, create_structs=create_structs)
|
||
if elem_type is not None:
|
||
return ir.ArrayType(elem_type, size)
|
||
|
||
typedef_names = {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'}
|
||
# 类型名内允许 '*':泛型特化名可带指针层实参(如
|
||
# %"<模sha1>.list[<sha1>.AST*]"*)。此前字符集缺 '*' 会把名字截断在
|
||
# 内层 '*' 处(丢掉尾随 ']'),随后 gc_base/gc_args 重解析出
|
||
# list[<sha1>.AST](少一层指针)并 declare_only 特化出无方法体的
|
||
# 假类型 → 调用点引用未定义的 <模sha1>.list[<sha1>.AST].__len__
|
||
# (链接期 undefined symbol 根因)。
|
||
struct_match = re.match(r'%"?((?:[a-f0-9]+\.)?[\w.\[\]*]+)"?', type_str)
|
||
if struct_match:
|
||
raw_name = struct_match.group(1)
|
||
actual_source_sha1 = source_sha1
|
||
if '.' in raw_name:
|
||
parts = raw_name.split('.', 1)
|
||
if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]):
|
||
actual_source_sha1 = parts[0]
|
||
struct_name = parts[1]
|
||
else:
|
||
struct_name = raw_name
|
||
if struct_name not in typedef_names:
|
||
# 泛型类特化(如 list[str]):触发特化创建结构体和方法声明
|
||
# 使用 declare_only=True 避免在引用模块中生成方法体(定义由定义模块提供)
|
||
if '[' in struct_name and actual_source_sha1:
|
||
if struct_name in Gen.structs:
|
||
return Gen.structs[struct_name]
|
||
gc_base: str = struct_name.split('[')[0]
|
||
gc_args_raw: str = struct_name[len(gc_base):]
|
||
gc_type_args: list[str] = []
|
||
for gc_arg_part in gc_args_raw.split(']['):
|
||
gc_arg_part = gc_arg_part.strip('[]')
|
||
if gc_arg_part:
|
||
gc_type_args.append(gc_arg_part)
|
||
if (gc_type_args
|
||
and hasattr(self.Trans, 'ClassHandler')
|
||
and hasattr(self.Trans.ClassHandler, '_generic_class_templates')
|
||
and gc_base in self.Trans.ClassHandler._generic_class_templates):
|
||
gc_type_names: list[str] = []
|
||
for gc_ta in gc_type_args:
|
||
gc_tn: str = gc_ta
|
||
if gc_ta == 'int':
|
||
gc_tn = 'CInt'
|
||
elif gc_ta == 'double':
|
||
gc_tn = 'CDouble'
|
||
elif gc_ta == 'float':
|
||
gc_tn = 'CFloat'
|
||
elif gc_ta == 'char':
|
||
gc_tn = 'CChar'
|
||
elif gc_ta == 'bool':
|
||
gc_tn = 'CBool'
|
||
gc_type_names.append(gc_tn)
|
||
saved_module_sha1: str | None = getattr(Gen, 'module_sha1', None)
|
||
saved_trans_sha1: str | None = getattr(self.Trans, '_module_sha1', None)
|
||
Gen.module_sha1 = actual_source_sha1
|
||
self.Trans._module_sha1 = actual_source_sha1
|
||
try:
|
||
gc_spec: str | None = self.Trans.ClassHandler._specialize_generic_class(
|
||
gc_base, gc_type_args, Gen, type_names=gc_type_names,
|
||
declare_only=True)
|
||
except Exception:
|
||
gc_spec = None
|
||
Gen.module_sha1 = saved_module_sha1
|
||
self.Trans._module_sha1 = saved_trans_sha1
|
||
if gc_spec and gc_spec in Gen.structs:
|
||
return Gen.structs[gc_spec]
|
||
if create_structs:
|
||
return Gen._get_or_create_struct(struct_name, source_sha1=actual_source_sha1)
|
||
else:
|
||
existing = Gen.structs.get(struct_name)
|
||
if existing is not None:
|
||
return existing
|
||
# 尝试用 source_sha1 前缀的全名查找
|
||
if actual_source_sha1:
|
||
full_key = f"{actual_source_sha1}.{struct_name}"
|
||
existing_full = Gen.structs.get(full_key)
|
||
if existing_full is not None:
|
||
return existing_full
|
||
return None
|
||
return ir.IntType(32)
|
||
|
||
return None
|
||
|
||
def _strip_param_name(self, param_str: str) -> str:
|
||
"""从 LLVM 参数字符串中剥离参数名,只保留类型部分
|
||
例如: '%"sha1.struct_name"* %param_name' -> '%"sha1.struct_name"*'
|
||
'i8* %path' -> 'i8*'
|
||
'i32' -> 'i32'
|
||
"""
|
||
param_str = param_str.strip()
|
||
stripped = re.sub(r'\s+%[\w.]+\s*$', '', param_str)
|
||
return stripped.strip()
|