fix(sizeof,parse): 尺寸链去静默兜底,解析报错可定位,补 C 解析器缺口
StructGen: _get_struct_size/_get_align 去 opaque→8/ptr_size、未知→4 兜底并补 VectorType;LiteralStructType.fields→.elements(llvmlite 无 fields,必然 AttributeError) HandlesClassDef: union 载荷尺寸经不存在的 ir.DataLayout 静默退化成 8 字节;HandlesExprCall/ExprBuiltin: sizeof 各静默 0 分支改 fail-fast,数组元素 sizeof 统一走 _get_struct_size HandlesFor: nonlocal struct/array 堆分配不再退化成 malloc(8) vast/vast_c: 补一元 * / & 解析,装饰器后 extern/export 分派修复,C 侧报错带 token 与行号;StubGen.Converter: 宏展开丢尾随空行修 .pyi 缩进非法 其余为 rawptr 迁移 / fail-fast 口径等历史改动一并入库
This commit is contained in:
1 parent
53a345a59a
commit
c83fc752c6
53 files changed
+6227
-876
No files matched your search
@@ -0,0 +1,152 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""只读审计:全仓"把源码交给 Python 解析器"的调用点是否都过了 Viper 软关键字层。
|
||||||
|
|
||||||
|
背景(为什么需要这个门禁)
|
||||||
|
Viper 前端 = TPC 自研 vast(`lib/vast`)+ **唯一展开点**
|
||||||
|
`lib/core/SymbolUtils.ExpandSoftKeywords`(`fn/fun/func/function` → `def`;
|
||||||
|
`enum X:` 块 → `class X(t.CEnum):`,两者均同行替换、行号不变)。
|
||||||
|
凡把 **Viper 源码文本**交给 `ast.parse` / `vast.parse` 的入口都必须先过这一层;
|
||||||
|
漏过者的失败形态是**静默降级**,不是报错:`includes/ast/base.vp:168` 的
|
||||||
|
`enum ASTFlag:` 曾让整个 base.vp 的声明(含顶层 `_init_ast`)一个都不注册,
|
||||||
|
下游只报 `Undefined function: '_init_ast'`(浪费一轮排查,见
|
||||||
|
`Debugger/enum-semantics.md` §13)。判据 = `Grep "ast\\.parse\\("` 逐个确认
|
||||||
|
上游是否已有展开层,或该输入**必然无糖**。
|
||||||
|
|
||||||
|
分类(逐调用点,见 `Classify`)
|
||||||
|
PASS 首实参文本含 `ExpandSoftKeywords`(同一表达式内已展开)
|
||||||
|
EVAL `mode='eval'`:注解 / 表达式的**片段**解析,不是整份源码 ⇒ 无需
|
||||||
|
EXEMPT 命中下方冻结豁免表:输入必然是 `.vpi`/`.pyi` 无糖产物,或同一函数
|
||||||
|
上游(紧邻几行)已展开 ⇒ 已人工审计确认
|
||||||
|
SUSPECT 其余 ⇒ **需人工判定**;`--strict` 下 exit 1(门禁失败)
|
||||||
|
|
||||||
|
覆盖范围:仓库内全部 `*.py`(含根目录脚本、`MCP/**`、`TransPyC/Tools/**`),
|
||||||
|
跳过 `.git`/`__pycache__`/`output`/`temp`/`.tpv_build`/`dlls` 与解析器自身
|
||||||
|
(`lib/vast`、`lib/vast_c` 是前端**实现**,不是前端**消费者**)。
|
||||||
|
TPV 侧源码(`TransPyV/**` 的 `.vp`)跑自己的前端,不在本脚本范围内。
|
||||||
|
|
||||||
|
用法(**只读,不写任何文件**)
|
||||||
|
python TransPyC/Tools/AuditViperParseEntries.py # 打印审计表
|
||||||
|
python TransPyC/Tools/AuditViperParseEntries.py --strict # 有 SUSPECT 则 exit 1
|
||||||
|
"""
|
||||||
|
import ast as pyast
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
ScriptDir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
RepoRoot = os.path.dirname(os.path.dirname(ScriptDir))
|
||||||
|
|
||||||
|
SkipDirs = {'__pycache__', '.git', '.tpv_build', 'dlls', 'output', 'temp', 'node_modules'}
|
||||||
|
# 前端实现(vast 解析器本体;它自己就是"唯一许可入口"的实现层)
|
||||||
|
SkipRelPrefixes = ('TransPyC/lib/vast/', 'TransPyC/lib/vast_c/')
|
||||||
|
|
||||||
|
# ---- 冻结豁免表:(仓库相对路径, 首实参文本归一化) -> 豁免理由 ------------------
|
||||||
|
# 新增合法调用点时必须在此登记原因;否则 --strict 会把新调用点当作 SUSPECT 拦下。
|
||||||
|
# 表来源:本轮全仓穷举审计(见最终报告 B 节审计表)。
|
||||||
|
Exempt = {
|
||||||
|
('TransPyC/lib/Projectrans/Phase2Translator.py', 'f.read()'):
|
||||||
|
'.vpi/.pyi 无糖产物(_ScanPyiFiles / _PyiCacheWorkerInitializer 仅扫 .vpi/.pyi)',
|
||||||
|
('TransPyC/lib/Projectrans/Phase2Translator.py', 'sig_content'):
|
||||||
|
'sig_files 全部来自 temp/*.vpi|*.pyi(Phase2Translator._LoadSha1Map:759)',
|
||||||
|
('TransPyC/lib/Projectrans/DeclarationGenerator.py', 'pyi_content'):
|
||||||
|
'generate(pyi_content, ...) 的入参来自 Phase1Generator 读 temp/{sha1}.vpi',
|
||||||
|
('TransPyC/lib/StubGen/Converter.py', 'PyContent'):
|
||||||
|
'convert() 首行 PyContent = ExpandSoftKeywords(PyContent)(Converter.py:68)',
|
||||||
|
('TransPyC/lib/Shell/TransPyC.py', 'content'):
|
||||||
|
'同函数 :100 content = ExpandSoftKeywords(content)',
|
||||||
|
('TransPyC/lib/Shell/TransPyC.py', 'Content'):
|
||||||
|
'同函数 :445 Content = ExpandSoftKeywords(Content)',
|
||||||
|
('TransPyC/lib/Shell/TransPyC.py', 'self.code'):
|
||||||
|
'Convert() :522 self.code = ExpandSoftKeywords(self.code)',
|
||||||
|
('TransPyC/lib/core/Translator/PythonParser.py', 'content'):
|
||||||
|
'ParsePythonFile() :76 content = ExpandSoftKeywords(content)',
|
||||||
|
('TransPyC/lib/core/Translator/AnnotationLoader.py', 'ann_content'):
|
||||||
|
'三处调用点各自紧邻上一行 ann_content = ExpandSoftKeywords(ann_content)',
|
||||||
|
('TransPyC/lib/core/SymbolExtractor.py', 'code'):
|
||||||
|
'extract(FilePath) 的 FilePath 全部来自 LoadModuleSymbols → .vpi/.pyi',
|
||||||
|
('TransPyC/lib/core/Handles/HandlesImports.py', '_fh0.read()'):
|
||||||
|
'.vpi(_TryLoadClassMembersFromPyi guard 分支补解析 class_parents)',
|
||||||
|
('TransPyC/lib/core/Handles/HandlesImports.py', 'f.read()'):
|
||||||
|
'.vpi(_TryLoadClassMembersFromPyi 主路径 _pyi_cache 填充)',
|
||||||
|
('TransPyC/lib/core/Handles/HandlesExprCall.py', 'pyi_code'):
|
||||||
|
'.vpi(_vtable_pyi_cache 填充,路径固定 temp/{sha1}.vpi)',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def Classify(Rel, Arg, Mode):
|
||||||
|
"""→ (类别, 理由)。"""
|
||||||
|
if 'ExpandSoftKeywords' in Arg:
|
||||||
|
return 'PASS', '首实参已过 ExpandSoftKeywords'
|
||||||
|
if Mode.strip('\'"') == 'eval':
|
||||||
|
return 'EVAL', "mode='eval'(注解/表达式片段,非整份源码)"
|
||||||
|
Hit = Exempt.get((Rel, Arg))
|
||||||
|
if Hit:
|
||||||
|
return 'EXEMPT', Hit
|
||||||
|
return 'SUSPECT', '未过展开层且不在冻结豁免表中(需人工判定该输入是否必然无糖)'
|
||||||
|
|
||||||
|
|
||||||
|
def IterCallSites():
|
||||||
|
"""遍历仓库 .py,产出 (rel, line, arg, mode);无法按 CPython 解析的文件记入 Unparsed。"""
|
||||||
|
for Root, Dirs, Files in os.walk(RepoRoot):
|
||||||
|
Dirs[:] = sorted(d for d in Dirs if d not in SkipDirs)
|
||||||
|
for Name in sorted(Files):
|
||||||
|
if not Name.endswith('.py'):
|
||||||
|
continue
|
||||||
|
Path = os.path.join(Root, Name)
|
||||||
|
Rel = os.path.relpath(Path, RepoRoot).replace(os.sep, '/')
|
||||||
|
if Rel.startswith(SkipRelPrefixes):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
with open(Path, encoding='utf-8') as Fh:
|
||||||
|
Src = Fh.read()
|
||||||
|
Tree = pyast.parse(Src, Rel)
|
||||||
|
except Exception as Err:
|
||||||
|
yield ('<unparsed>', 0, Rel, type(Err).__name__)
|
||||||
|
continue
|
||||||
|
for Node in pyast.walk(Tree):
|
||||||
|
if not isinstance(Node, pyast.Call):
|
||||||
|
continue
|
||||||
|
Func = Node.func
|
||||||
|
if not isinstance(Func, pyast.Attribute) or Func.attr != 'parse':
|
||||||
|
continue
|
||||||
|
if not (isinstance(Func.value, pyast.Name) and Func.value.id in ('ast', 'vast')):
|
||||||
|
continue
|
||||||
|
Arg = pyast.get_source_segment(Src, Node.args[0]) if Node.args else ''
|
||||||
|
Mode = ''
|
||||||
|
for Kw in Node.keywords:
|
||||||
|
if Kw.arg == 'mode':
|
||||||
|
Mode = pyast.get_source_segment(Src, Kw.value) or ''
|
||||||
|
yield ('site', Node.lineno, Rel, ' '.join((Arg or '').split()), ' '.join(Mode.split()))
|
||||||
|
|
||||||
|
|
||||||
|
def Main(Strict):
|
||||||
|
Rows = []
|
||||||
|
Unparsed = []
|
||||||
|
for Item in IterCallSites():
|
||||||
|
if Item[0] == '<unparsed>':
|
||||||
|
Unparsed.append((Item[2], Item[3]))
|
||||||
|
continue
|
||||||
|
_, Line, Rel, Arg, Mode = Item
|
||||||
|
Kind, Why = Classify(Rel, Arg, Mode)
|
||||||
|
Rows.append((Kind, Rel, Line, Arg, Why))
|
||||||
|
Order = {'SUSPECT': 0, 'PASS': 1, 'EVAL': 2, 'EXEMPT': 3}
|
||||||
|
Rows.sort(key=lambda R: (Order[R[0]], R[1], R[2]))
|
||||||
|
Count = {}
|
||||||
|
for Kind, _, _, _, _ in Rows:
|
||||||
|
Count[Kind] = Count.get(Kind, 0) + 1
|
||||||
|
for Kind, Rel, Line, Arg, Why in Rows:
|
||||||
|
print('%-8s %s:%d arg=%s — %s' % (Kind, Rel, Line, (Arg or '')[:60], Why))
|
||||||
|
print('-' * 100)
|
||||||
|
print('sites=%d PASS=%d EVAL=%d EXEMPT=%d SUSPECT=%d unparsed=%d' % (
|
||||||
|
len(Rows), Count.get('PASS', 0), Count.get('EVAL', 0),
|
||||||
|
Count.get('EXEMPT', 0), Count.get('SUSPECT', 0), len(Unparsed)))
|
||||||
|
if Unparsed:
|
||||||
|
print('无法按 CPython 解析(未审计): %s' % Unparsed)
|
||||||
|
if Strict and Count.get('SUSPECT'):
|
||||||
|
sys.stderr.write('[strict] 门禁失败: 有 %d 个未过展开层且未登记的解析入口\n'
|
||||||
|
% Count['SUSPECT'])
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(Main('--strict' in sys.argv[1:]))
|
||||||
@@ -0,0 +1,840 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""作用域感知静态扫描: `rawptr[<族>.<X>]` 变量上访问 <X> 不存在的字段(fast-fail 潜在点)。
|
||||||
|
|
||||||
|
族(Family)= 一个"类名限定前缀 + 类定义来源"。默认族表见 BuildFamilies;
|
||||||
|
`rawptr[ast.X]` / `rawptr[llvmlite.X]` / `rawptr[GenTypes.X]` / `rawptr[HT.X]` /
|
||||||
|
`rawptr[memhub.X]` 均可解析。另加载 linkedlist.vp 作为**支撑族**(空前缀 ''),
|
||||||
|
供 `class ParamNode(GSListNode[ParamNode])` 这类**裸基类名**解析(否则 `.Next` 会误报)。
|
||||||
|
|
||||||
|
相对旧版的关键修正(旧版按“变量名全文件聚合 + 正则硬匹配”导致大量误报):
|
||||||
|
1) 作用域建模: 为 模块 / 函数(方法/嵌套函数) / lambda 建立独立作用域,
|
||||||
|
类体作用域按 Python 语义对方法“穿透”; 属性访问按“该访问点所在作用域链”找
|
||||||
|
**最近一次、且位置在访问点之前** 的声明(不是“全文件第一条”)。
|
||||||
|
2) 形参(含 self 与 rawptr[X] 形参)计入声明; 无注解形参视为“遮蔽”外层同名变量。
|
||||||
|
3) 重新赋值/重声明: AnnAssign / Assign(cast 形态) / 赋值继承(Name RHS) 都参与;
|
||||||
|
非指针赋值记录为“已解析但非指针”=> 不再继承更早的类。
|
||||||
|
4) 跳过非代码: 解析源码后, 注释/字符串/docstring 内容天然不产生 Attribute 节点
|
||||||
|
(如 "%s.__iter__" 之类的字符串与三引号注释块不再命中)。
|
||||||
|
5) 类成员表按族隔离: 基类先在**本族**找, 再找**支撑族**(''), 最后全局唯一名;
|
||||||
|
同一基类名跨族重复时报 AMBIG(宁可 UNRESOLVED 也不猜)。
|
||||||
|
6) `t.REnum`(tagged union)派生类: 变体字段合并进父类成员集
|
||||||
|
(TPV 允许在父类指针上访问变体字段, 不合并会产生大量误报)。
|
||||||
|
|
||||||
|
前端 = **TPC 自研 vast**(`lib/vast`,与 StubGen/Converter.py 用的 `import vast as ast`
|
||||||
|
是同一源文件; 不可用 CPython `ast`, 否则含 `list[X*]` 等 vast 专有写法的文件会整体
|
||||||
|
SyntaxError 被跳过)。vast 与 CPython ast 的差异及本脚本的适配:
|
||||||
|
A) `X*` / `X**` 被 vast 脱糖成 `X | t.CPtr`(`**` 加两层), 其中合成的
|
||||||
|
`t.CPtr` Attribute 节点**连 lineno 属性都没有**(编译器内部脱糖产物, 不是源码里的
|
||||||
|
访问; 全树实测 555 个, 全部是 ('t','CPtr'))=> 计入 Uses 时用
|
||||||
|
`getattr(Node,'lineno',None) is not None` 排除。
|
||||||
|
B) vast 的 SyntaxError 可能不带 lineno(C 路径)=> 从报文 `at line N` 兜底提取。
|
||||||
|
C) vast 的装饰器 `@t.NoVTable` 的 col_offset 是 `@` 的位置(CPython 是其后 1 列);
|
||||||
|
该 Attribute 的接收者 `t` 不是族指针, 判据不受影响。
|
||||||
|
D) 其余节点类名/属性名/walk/iter_child_nodes 与 CPython ast 同构(已逐节点对照)。
|
||||||
|
|
||||||
|
输出: 文件:行号 / 变量 / 推导出的类 / 字段 / 声明来源行 / 判定, 并统计
|
||||||
|
NOWHERE / OTHER / RESOLVED / UNRESOLVED, 并按族(ast/llvmlite/其它)分桶。
|
||||||
|
|
||||||
|
路径: 全部按**脚本自身位置**推导(ScriptDir = <RepoRoot>/TransPyC/Tools),
|
||||||
|
不依赖 cwd;可用 --app-root / --includes / --out 覆盖。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python TransPyC/Tools/ScanAstFieldMiss.py [--app-root DIR] [--includes DIR]
|
||||||
|
[--out FILE] [--verbose] [--strict]
|
||||||
|
python TransPyC/Tools/ScanAstFieldMiss.py --selftest [--includes DIR] [--out FILE]
|
||||||
|
|
||||||
|
默认值:
|
||||||
|
--app-root <RepoRoot>/TransPyV/App
|
||||||
|
--includes <RepoRoot>/includes
|
||||||
|
--out <RepoRoot>/Debugger/ScanAstFieldMiss.txt
|
||||||
|
|
||||||
|
退出码:
|
||||||
|
0 非 --strict 的正常扫描; 或 --strict 下 NOWHERE / OTHER / parsefail 三者全 0
|
||||||
|
1 --strict 下 有 NOWHERE/OTHER 命中, **或 parsefail > 0**
|
||||||
|
(parsefail 也算门禁失败: 文件被前端静默跳过 = 假绿, 见下)
|
||||||
|
2 参数错误
|
||||||
|
--selftest: 0 = 断言全通过; 1 = 有断言失败
|
||||||
|
|
||||||
|
--selftest 为什么必须有(历史教训):
|
||||||
|
上一版门禁的前端是 CPython `ast`, 对含 `list[X*]` / `rawptr[X]*` 等 vast 专有写法的
|
||||||
|
文件整体 SyntaxError 跳过 ⇒ 曾出现 "OTHER=0 全绿" 而工作树里实际有 **18 处**
|
||||||
|
`rawptr[llvmlite.Function].Ty` 真错(整批文件根本没被检查)。
|
||||||
|
故 --selftest 内置合成用例(生成到临时目录、跑完即删), 断言"**报出集合 == 预期集合**",
|
||||||
|
覆盖 4 类正例(含 vast 专有 `rawptr[X]*` / `rawptr[X]**`)与 4 类负例
|
||||||
|
(合法字段 / 跨函数同名各自声明 / docstring 与注释 / 返回值推断生效),
|
||||||
|
用来证明扫描器不是空转。
|
||||||
|
"""
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 0. 路径推导(全部按脚本自身位置, 不依赖 cwd)
|
||||||
|
# ScriptDir = <RepoRoot>/TransPyC/Tools
|
||||||
|
# TpcRoot = <RepoRoot>/TransPyC (TransPyC 仓库根)
|
||||||
|
# RepoRoot = <RepoRoot> (工作区根)
|
||||||
|
# ============================================================
|
||||||
|
ScriptDir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
TpcRoot = os.path.dirname(ScriptDir)
|
||||||
|
RepoRoot = os.path.dirname(TpcRoot)
|
||||||
|
|
||||||
|
DefaultAppRoot = os.path.join(RepoRoot, 'TransPyV', 'App')
|
||||||
|
DefaultIncludesDir = os.path.join(RepoRoot, 'includes')
|
||||||
|
DefaultOutFile = os.path.join(RepoRoot, 'Debugger', 'ScanAstFieldMiss.txt')
|
||||||
|
|
||||||
|
# ---- 前端: TPC 自研 vast(非 CPython ast)。需要 TransPyC 包根在 sys.path,
|
||||||
|
# lib._bootstrap 会再补齐 lib/ 与 lib/includes(使 `import vast` 可用)。
|
||||||
|
if TpcRoot not in sys.path:
|
||||||
|
sys.path.insert(0, TpcRoot)
|
||||||
|
from lib import vast as ast # noqa: E402
|
||||||
|
from lib.core.SymbolUtils import ExpandSoftKeywords # noqa: E402
|
||||||
|
|
||||||
|
SupportPrefix = ''
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 1. 类成员表: 字段 / 方法 / 基类(按族隔离;每次扫描重建)
|
||||||
|
# ============================================================
|
||||||
|
FamilyTable = []
|
||||||
|
SupportFiles = []
|
||||||
|
FamilyNames = set()
|
||||||
|
|
||||||
|
ClassFields = {}
|
||||||
|
ClassMethods = {}
|
||||||
|
ClassBase = {}
|
||||||
|
ClassLoc = {} # 裸类名 -> [族, ...](用于跨族基类解析)
|
||||||
|
EnumClass = {} # (族, 类名) -> 1 表示 t.REnum/t.CEnum 派生(变体字段合并)
|
||||||
|
REnumClass = {} # (族, 类名) -> 1 表示 t.REnum 派生(额外有编译器内建 .kind())
|
||||||
|
|
||||||
|
MemberCache = {}
|
||||||
|
FamilyFields = {} # 族 -> 该族所有字段并集(OTHER/NOWHERE 判定用)
|
||||||
|
AmbiguousBases = set()
|
||||||
|
|
||||||
|
Uses = [] # [(AttrNode, Scope)];Walk 现场填充, 每个文件扫描前 clear
|
||||||
|
|
||||||
|
# ---- 返回注解表(同文件内 def f(...) -> rawptr[族.X]):供 x = f() 形态的类型推导。
|
||||||
|
# 仅补充覆盖(收紧),不改变任何既有判据:名字在文件内返回注解不一致时报歧义并放弃。
|
||||||
|
FuncRet = {}
|
||||||
|
|
||||||
|
|
||||||
|
def ResetTables():
|
||||||
|
"""清空全部类表/缓存(供同一进程内多轮扫描复用, 如 --selftest)。"""
|
||||||
|
global FamilyTable, SupportFiles, FamilyNames
|
||||||
|
global ClassFields, ClassMethods, ClassBase, ClassLoc, EnumClass, REnumClass
|
||||||
|
global MemberCache, FamilyFields, AmbiguousBases
|
||||||
|
FamilyTable = []
|
||||||
|
SupportFiles = []
|
||||||
|
FamilyNames = set()
|
||||||
|
ClassFields = {}
|
||||||
|
ClassMethods = {}
|
||||||
|
ClassBase = {}
|
||||||
|
ClassLoc = {}
|
||||||
|
EnumClass = {}
|
||||||
|
REnumClass = {}
|
||||||
|
MemberCache = {}
|
||||||
|
FamilyFields = {}
|
||||||
|
AmbiguousBases = set()
|
||||||
|
FuncRet.clear()
|
||||||
|
|
||||||
|
|
||||||
|
def AddClass(Fam, Name, Bases):
|
||||||
|
ClassFields.setdefault((Fam, Name), set())
|
||||||
|
ClassMethods.setdefault((Fam, Name), set())
|
||||||
|
ClassLoc.setdefault(Name, [])
|
||||||
|
if Fam not in ClassLoc[Name]:
|
||||||
|
ClassLoc[Name].append(Fam)
|
||||||
|
if Bases:
|
||||||
|
ClassBase.setdefault((Fam, Name), set()).update(Bases)
|
||||||
|
|
||||||
|
|
||||||
|
def BaseName(Node):
|
||||||
|
if isinstance(Node, ast.Name):
|
||||||
|
return Node.id
|
||||||
|
if isinstance(Node, ast.Attribute):
|
||||||
|
return Node.attr
|
||||||
|
if isinstance(Node, ast.Subscript): # GSListNode[ParamNode] -> GSListNode
|
||||||
|
return BaseName(Node.value)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def CollectBody(Fam, Name, Body, Enum):
|
||||||
|
"""收集类体成员;Enum=True 时把嵌套变体类的字段并入本类(REnum 语义)。"""
|
||||||
|
for Item in Body:
|
||||||
|
if isinstance(Item, ast.AnnAssign) and isinstance(Item.target, ast.Name):
|
||||||
|
ClassFields[(Fam, Name)].add(Item.target.id)
|
||||||
|
elif isinstance(Item, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||||
|
ClassMethods[(Fam, Name)].add(Item.name)
|
||||||
|
elif Enum and isinstance(Item, ast.ClassDef):
|
||||||
|
CollectBody(Fam, Name, Item.body, Enum) # 变体字段并入父类
|
||||||
|
|
||||||
|
|
||||||
|
def RegisterClass(Fam, Node):
|
||||||
|
Bases = set()
|
||||||
|
for B in Node.bases:
|
||||||
|
Nm = BaseName(B)
|
||||||
|
if Nm:
|
||||||
|
Bases.add(Nm)
|
||||||
|
AddClass(Fam, Node.name, Bases)
|
||||||
|
Enum = 1 if (Bases & {'REnum', 'CEnum'}) else 0
|
||||||
|
if Enum:
|
||||||
|
EnumClass[(Fam, Node.name)] = 1
|
||||||
|
if 'REnum' in Bases:
|
||||||
|
# REnum.kind() 是编译器内建(HandlesExprCall.py:3126 'kind' → __tag 读取)
|
||||||
|
REnumClass[(Fam, Node.name)] = 1
|
||||||
|
CollectBody(Fam, Node.name, Node.body, Enum)
|
||||||
|
|
||||||
|
|
||||||
|
def ParseSrc(Src, Path):
|
||||||
|
"""vast 前端解析入口(全脚本单一前端; 失败抛异常, 由调用方记账)。
|
||||||
|
|
||||||
|
`.vp` 源码须先过 Viper 软关键字层(`lib/core/SymbolUtils.ExpandSoftKeywords`:
|
||||||
|
`fn/fun/func/function` → `def`、`enum X:` → `class X(t.CEnum):`),与
|
||||||
|
`StubGen/Converter.py` / `Shell/TransPyC.py` 等既有前端入口同口径。
|
||||||
|
否则含 `enum` 的 `.vp`(如 `includes/ast/base.vp:168`)会在此整体 SyntaxError,
|
||||||
|
被 `LoadInclude` 的正则兜底或 `RunScan` 的 `parsefail` 记成"解析失败"——两者
|
||||||
|
都会**静默漏掉该文件的类/字段**(本脚本的假绿来源)。`.py` 无糖,展开为空操作
|
||||||
|
(`'enum' in Code` 门禁 + 行首锚定正则)。
|
||||||
|
"""
|
||||||
|
return ast.parse(ExpandSoftKeywords(Src), Path)
|
||||||
|
|
||||||
|
|
||||||
|
def ErrLine(E):
|
||||||
|
"""vast 的 SyntaxError 可能无 lineno(C 路径)=> 从报文 `at line N` 兜底提取。"""
|
||||||
|
L = getattr(E, 'lineno', None)
|
||||||
|
if L:
|
||||||
|
return L
|
||||||
|
M = re.search(r'at line (\d+)', str(E))
|
||||||
|
return int(M.group(1)) if M else -1
|
||||||
|
|
||||||
|
|
||||||
|
def LoadIncludeRegex(Src, Fam):
|
||||||
|
"""ast.parse 失败(docstring 含 \\xNN 等)时的回退:缩进感知的类/字段/方法收集。"""
|
||||||
|
Indent = {}
|
||||||
|
Enum = {}
|
||||||
|
for Ln in Src.splitlines():
|
||||||
|
M = re.match(r'(\s*)class (\w+)(?:\[[^\]]*\])?(?:\(([^)]*)\))?\s*:', Ln)
|
||||||
|
if M:
|
||||||
|
Depth = len(M.group(1))
|
||||||
|
Name = M.group(2)
|
||||||
|
Bases = set()
|
||||||
|
if M.group(3):
|
||||||
|
for B in M.group(3).split(','):
|
||||||
|
B = B.strip().split('.')[-1].split('[')[0].strip()
|
||||||
|
if B:
|
||||||
|
Bases.add(B)
|
||||||
|
AddClass(Fam, Name, Bases)
|
||||||
|
Indent[Name] = Depth
|
||||||
|
Enum[Name] = 1 if (Bases & {'REnum', 'CEnum'}) else 0
|
||||||
|
if Enum[Name]:
|
||||||
|
EnumClass[(Fam, Name)] = 1
|
||||||
|
continue
|
||||||
|
M2 = re.match(r'(\s+)(\w+)\s*:\s', Ln)
|
||||||
|
if M2 and Indent:
|
||||||
|
Depth = len(M2.group(1))
|
||||||
|
Owner = [N for N, D in Indent.items() if D < Depth]
|
||||||
|
if Owner:
|
||||||
|
P = max(Owner, key=lambda N: Indent[N])
|
||||||
|
ClassFields[(Fam, P)].add(M2.group(2))
|
||||||
|
continue
|
||||||
|
M3 = re.match(r'(\s+)def (\w+)\(', Ln)
|
||||||
|
if M3 and Indent:
|
||||||
|
Depth = len(M3.group(1))
|
||||||
|
Owner = [N for N, D in Indent.items() if D < Depth]
|
||||||
|
if Owner:
|
||||||
|
P = max(Owner, key=lambda N: Indent[N])
|
||||||
|
ClassMethods[(Fam, P)].add(M3.group(2))
|
||||||
|
|
||||||
|
|
||||||
|
def LoadInclude(Path, Fam):
|
||||||
|
try:
|
||||||
|
Src = open(Path, encoding='utf-8').read()
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
Tree = ParseSrc(Src, Path)
|
||||||
|
except Exception: # noqa: BLE001 (vast 失败可能是 SyntaxError/ValueError)
|
||||||
|
LoadIncludeRegex(Src, Fam)
|
||||||
|
return
|
||||||
|
for Node in Tree.body:
|
||||||
|
if isinstance(Node, ast.ClassDef):
|
||||||
|
RegisterClass(Fam, Node)
|
||||||
|
|
||||||
|
|
||||||
|
def BuildFamilies(AppRoot, IncludesDir):
|
||||||
|
"""按 AppRoot/IncludesDir 建族表并加载类定义(含支撑族)。"""
|
||||||
|
global FamilyTable, SupportFiles, FamilyNames
|
||||||
|
FamilyTable = [
|
||||||
|
('ast', [os.path.join(IncludesDir, 'ast')]),
|
||||||
|
('llvmlite', [os.path.join(IncludesDir, 'llvmlite')]),
|
||||||
|
('GenTypes', [os.path.join(AppRoot, 'lib', 'core', 'Gen', 'GenTypes.vp')]),
|
||||||
|
('HT', [os.path.join(AppRoot, 'lib', 'core', 'Gen', 'Translator.vp')]),
|
||||||
|
('memhub', [os.path.join(IncludesDir, 'memhub.vp')]),
|
||||||
|
]
|
||||||
|
SupportFiles = [os.path.join(IncludesDir, 'linkedlist.vp')]
|
||||||
|
FamilyNames = set(P for P, _ in FamilyTable) | {''}
|
||||||
|
for Prefix, Paths in FamilyTable:
|
||||||
|
for P in Paths:
|
||||||
|
if os.path.isdir(P):
|
||||||
|
for F in sorted(glob.glob(os.path.join(P, '*.vp'))):
|
||||||
|
LoadInclude(F, Prefix)
|
||||||
|
else:
|
||||||
|
LoadInclude(P, Prefix)
|
||||||
|
for F in SupportFiles:
|
||||||
|
LoadInclude(F, SupportPrefix)
|
||||||
|
|
||||||
|
|
||||||
|
def ComputeFamilyFields():
|
||||||
|
global FamilyFields
|
||||||
|
FamilyFields = {}
|
||||||
|
for (_F, _N) in ClassFields:
|
||||||
|
FamilyFields.setdefault(_F, set()).update(ClassFields[(_F, _N)])
|
||||||
|
|
||||||
|
|
||||||
|
def FindClass(Name, Prefer):
|
||||||
|
"""裸类名 -> (族, 类名)。优先本族, 再支撑族, 最后全局唯一名。"""
|
||||||
|
if (Prefer, Name) in ClassFields:
|
||||||
|
return (Prefer, Name)
|
||||||
|
if (SupportPrefix, Name) in ClassFields:
|
||||||
|
return (SupportPrefix, Name)
|
||||||
|
Locs = ClassLoc.get(Name, [])
|
||||||
|
if len(Locs) == 1:
|
||||||
|
return (Locs[0], Name)
|
||||||
|
if len(Locs) > 1:
|
||||||
|
AmbiguousBases.add(Name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def Members(Fam, Name, Seen=None):
|
||||||
|
if Seen is None:
|
||||||
|
Seen = set()
|
||||||
|
Key = (Fam, Name)
|
||||||
|
if Key in Seen:
|
||||||
|
return set()
|
||||||
|
Seen.add(Key)
|
||||||
|
Out = set(ClassFields.get(Key, ())) | set(ClassMethods.get(Key, ()))
|
||||||
|
if Key in REnumClass:
|
||||||
|
Out.add('kind')
|
||||||
|
for B in ClassBase.get(Key, ()):
|
||||||
|
Loc = FindClass(B, Fam)
|
||||||
|
if Loc is not None:
|
||||||
|
if Loc in REnumClass:
|
||||||
|
Out.add('kind')
|
||||||
|
Out |= Members(Loc[0], Loc[1], Seen)
|
||||||
|
return Out
|
||||||
|
|
||||||
|
|
||||||
|
def Member(Fam, Name):
|
||||||
|
Key = (Fam, Name)
|
||||||
|
if Key not in MemberCache:
|
||||||
|
MemberCache[Key] = Members(Fam, Name)
|
||||||
|
return MemberCache[Key]
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 2. 类型推导: 从注解 / cast 调用里取 (族, 类名)
|
||||||
|
# ============================================================
|
||||||
|
def AnnClass(Ann):
|
||||||
|
"""注解里若为 rawptr[<族>.<X>](可被 `| t.CPtr` 包裹)返回 (族, X), 否则 None。"""
|
||||||
|
if Ann is None:
|
||||||
|
return None
|
||||||
|
if isinstance(Ann, ast.BinOp) and isinstance(Ann.op, ast.BitOr):
|
||||||
|
return AnnClass(Ann.left) or AnnClass(Ann.right)
|
||||||
|
if isinstance(Ann, ast.Subscript):
|
||||||
|
if isinstance(Ann.value, ast.Name) and Ann.value.id == 'rawptr':
|
||||||
|
Sl = Ann.slice
|
||||||
|
if isinstance(Sl, ast.Attribute) and isinstance(Sl.value, ast.Name) \
|
||||||
|
and Sl.value.id in FamilyNames and Sl.value.id != '':
|
||||||
|
return (Sl.value.id, Sl.attr)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def CastClass(Val):
|
||||||
|
"""`rawptr[<族>.<X>](...)` 形态的 cast 调用返回 (族, X), 否则 None。"""
|
||||||
|
if isinstance(Val, ast.Call) and isinstance(Val.func, ast.Subscript):
|
||||||
|
return AnnClass(Val.func)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def CollectFuncRets(Tree):
|
||||||
|
FuncRet.clear()
|
||||||
|
Ambig = set()
|
||||||
|
for Node in ast.walk(Tree):
|
||||||
|
if isinstance(Node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||||
|
C = AnnClass(Node.returns)
|
||||||
|
if Node.name in FuncRet and FuncRet[Node.name] != C:
|
||||||
|
Ambig.add(Node.name)
|
||||||
|
else:
|
||||||
|
FuncRet[Node.name] = C
|
||||||
|
for N in Ambig:
|
||||||
|
FuncRet.pop(N, None)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 3. 作用域模型
|
||||||
|
# ============================================================
|
||||||
|
class Scope:
|
||||||
|
__slots__ = ('Kind', 'Parent', 'Decls')
|
||||||
|
|
||||||
|
def __init__(self, Kind, Parent):
|
||||||
|
self.Kind = Kind # 'module' / 'func' / 'class'
|
||||||
|
self.Parent = Parent
|
||||||
|
self.Decls = {} # name -> [(Lineno, Col, Cls, DeclLine)]
|
||||||
|
|
||||||
|
def Add(self, Name, Lineno, Col, Cls):
|
||||||
|
self.Decls.setdefault(Name, []).append((Lineno, Col, Cls, Lineno))
|
||||||
|
|
||||||
|
|
||||||
|
def Resolve(Sc, Name, Lineno, Col):
|
||||||
|
"""沿作用域链找 Name 在 (Lineno, Col) 之前最近一次的声明。
|
||||||
|
|
||||||
|
返回 (Cls, DeclLine); Cls 为 None 表示“已解析但非族指针”或“未找到”。
|
||||||
|
类体作用域对嵌套函数穿透(Python 语义)。
|
||||||
|
"""
|
||||||
|
Cur = Sc
|
||||||
|
First = True
|
||||||
|
while Cur is not None:
|
||||||
|
if Cur.Kind != 'class' or First:
|
||||||
|
if Name in Cur.Decls:
|
||||||
|
Best = None
|
||||||
|
for D in Cur.Decls[Name]:
|
||||||
|
if (D[0], D[1]) <= (Lineno, Col):
|
||||||
|
if Best is None or (D[0], D[1]) > (Best[0], Best[1]):
|
||||||
|
Best = D
|
||||||
|
if Best is not None:
|
||||||
|
return Best[2], Best[3]
|
||||||
|
First = False
|
||||||
|
Cur = Cur.Parent
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def Walk(Node, Sc):
|
||||||
|
if Node is None:
|
||||||
|
return
|
||||||
|
if isinstance(Node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||||
|
for D in Node.decorator_list:
|
||||||
|
Walk(D, Sc)
|
||||||
|
A = Node.args
|
||||||
|
Formals = list(A.posonlyargs) + list(A.args) + list(A.kwonlyargs)
|
||||||
|
for P in Formals:
|
||||||
|
Walk(P.annotation, Sc)
|
||||||
|
for D in A.defaults:
|
||||||
|
Walk(D, Sc)
|
||||||
|
for D in A.kw_defaults:
|
||||||
|
Walk(D, Sc)
|
||||||
|
if A.vararg is not None:
|
||||||
|
Walk(A.vararg.annotation, Sc)
|
||||||
|
if A.kwarg is not None:
|
||||||
|
Walk(A.kwarg.annotation, Sc)
|
||||||
|
Walk(Node.returns, Sc)
|
||||||
|
FS = Scope('func', Sc)
|
||||||
|
for P in Formals:
|
||||||
|
FS.Add(P.arg, Node.lineno, 0, AnnClass(P.annotation))
|
||||||
|
if A.vararg is not None:
|
||||||
|
FS.Add(A.vararg.arg, Node.lineno, 0, AnnClass(A.vararg.annotation))
|
||||||
|
if A.kwarg is not None:
|
||||||
|
FS.Add(A.kwarg.arg, Node.lineno, 0, AnnClass(A.kwarg.annotation))
|
||||||
|
for S in Node.body:
|
||||||
|
Walk(S, FS)
|
||||||
|
return
|
||||||
|
if isinstance(Node, ast.Lambda):
|
||||||
|
A = Node.args
|
||||||
|
Formals = list(A.posonlyargs) + list(A.args) + list(A.kwonlyargs)
|
||||||
|
for P in Formals:
|
||||||
|
Walk(P.annotation, Sc)
|
||||||
|
for D in A.defaults:
|
||||||
|
Walk(D, Sc)
|
||||||
|
for D in A.kw_defaults:
|
||||||
|
Walk(D, Sc)
|
||||||
|
LS = Scope('func', Sc)
|
||||||
|
for P in Formals:
|
||||||
|
LS.Add(P.arg, Node.lineno, 0, AnnClass(P.annotation))
|
||||||
|
Walk(Node.body, LS)
|
||||||
|
return
|
||||||
|
if isinstance(Node, ast.ClassDef):
|
||||||
|
for D in Node.decorator_list:
|
||||||
|
Walk(D, Sc)
|
||||||
|
for B in Node.bases:
|
||||||
|
Walk(B, Sc)
|
||||||
|
for K in Node.keywords:
|
||||||
|
Walk(K.value, Sc)
|
||||||
|
CS = Scope('class', Sc)
|
||||||
|
for S in Node.body:
|
||||||
|
Walk(S, CS)
|
||||||
|
return
|
||||||
|
if isinstance(Node, ast.Attribute):
|
||||||
|
# 只收源码里真实存在的属性访问: vast 脱糖 `X*` 会合成 `t.CPtr` Attribute,
|
||||||
|
# 该节点**连 lineno 属性都没有**(全树 555 个, 全为 ('t','CPtr')), 不是源码
|
||||||
|
# 访问、也不能参与位置比较 => 用 getattr 显式排除。
|
||||||
|
if isinstance(Node.value, ast.Name) and getattr(Node, 'lineno', None) is not None:
|
||||||
|
Uses.append((Node, Sc))
|
||||||
|
Walk(Node.value, Sc)
|
||||||
|
return
|
||||||
|
if isinstance(Node, ast.AnnAssign):
|
||||||
|
if isinstance(Node.target, ast.Name):
|
||||||
|
Sc.Add(Node.target.id, Node.target.lineno, Node.target.col_offset,
|
||||||
|
AnnClass(Node.annotation))
|
||||||
|
elif isinstance(Node.target, ast.Attribute):
|
||||||
|
Walk(Node.target, Sc) # `x.y: T = ...` 的属性访问
|
||||||
|
Walk(Node.annotation, Sc)
|
||||||
|
Walk(Node.value, Sc)
|
||||||
|
return
|
||||||
|
if isinstance(Node, ast.Assign):
|
||||||
|
Cls = None
|
||||||
|
V = Node.value
|
||||||
|
if isinstance(V, ast.Call):
|
||||||
|
Cls = CastClass(V)
|
||||||
|
if Cls is None and isinstance(V.func, ast.Name):
|
||||||
|
Cls = FuncRet.get(V.func.id)
|
||||||
|
elif isinstance(V, ast.Name):
|
||||||
|
Cls, _ = Resolve(Sc, V.id, V.lineno, V.col_offset)
|
||||||
|
for T in Node.targets:
|
||||||
|
if isinstance(T, ast.Name):
|
||||||
|
Sc.Add(T.id, T.lineno, T.col_offset, Cls)
|
||||||
|
# 赋值**目标**位置的属性访问也要计入(如 `vtable_node.Ty = ...`)
|
||||||
|
Walk(T, Sc)
|
||||||
|
Walk(Node.value, Sc)
|
||||||
|
return
|
||||||
|
if isinstance(Node, ast.AugAssign):
|
||||||
|
if isinstance(Node.target, ast.Name):
|
||||||
|
Sc.Add(Node.target.id, Node.target.lineno, Node.target.col_offset, None)
|
||||||
|
Walk(Node.target, Sc)
|
||||||
|
Walk(Node.value, Sc)
|
||||||
|
return
|
||||||
|
if isinstance(Node, (ast.For, ast.AsyncFor)):
|
||||||
|
for T in ast.walk(Node.target):
|
||||||
|
if isinstance(T, ast.Name):
|
||||||
|
Sc.Add(T.id, Node.lineno, 0, None)
|
||||||
|
Walk(Node.iter, Sc)
|
||||||
|
for S in Node.body:
|
||||||
|
Walk(S, Sc)
|
||||||
|
for S in Node.orelse:
|
||||||
|
Walk(S, Sc)
|
||||||
|
return
|
||||||
|
if isinstance(Node, ast.NamedExpr):
|
||||||
|
if isinstance(Node.target, ast.Name):
|
||||||
|
Sc.Add(Node.target.id, Node.target.lineno, Node.target.col_offset, None)
|
||||||
|
Walk(Node.value, Sc)
|
||||||
|
return
|
||||||
|
for Ch in ast.iter_child_nodes(Node):
|
||||||
|
Walk(Ch, Sc)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 4. 扫描(一轮 = ResetTables + BuildFamilies + 遍历 AppRoot)
|
||||||
|
# ============================================================
|
||||||
|
def BucketOf(Fam):
|
||||||
|
return Fam if Fam in ('ast', 'llvmlite') else 'OTHER:' + (Fam or 'support')
|
||||||
|
|
||||||
|
|
||||||
|
def Bump(D, Key):
|
||||||
|
D[Key] = D.get(Key, 0) + 1
|
||||||
|
|
||||||
|
|
||||||
|
def RunScan(AppRoot, IncludesDir, OutFile, Verbose):
|
||||||
|
"""跑一轮扫描: 写报告文件 + 返回统计/命中集合(不打印)。"""
|
||||||
|
ResetTables()
|
||||||
|
BuildFamilies(AppRoot, IncludesDir)
|
||||||
|
ComputeFamilyFields()
|
||||||
|
|
||||||
|
Nowhere = []
|
||||||
|
Other = []
|
||||||
|
ResolvedCount = 0
|
||||||
|
UnresolvedCount = 0
|
||||||
|
Files = sorted(glob.glob(os.path.join(AppRoot, '**', '*.vp'), recursive=True))
|
||||||
|
ParseFail = []
|
||||||
|
|
||||||
|
for Path in Files:
|
||||||
|
try:
|
||||||
|
Src = open(Path, encoding='utf-8').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
Tree = ParseSrc(Src, Path)
|
||||||
|
except Exception as E: # noqa: BLE001 (vast 失败可能是 SyntaxError/ValueError)
|
||||||
|
ParseFail.append((os.path.relpath(Path, AppRoot), ErrLine(E), str(E)[:80]))
|
||||||
|
continue
|
||||||
|
RawLines = Src.splitlines()
|
||||||
|
Uses.clear()
|
||||||
|
CollectFuncRets(Tree)
|
||||||
|
MS = Scope('module', None)
|
||||||
|
for S in Tree.body:
|
||||||
|
Walk(S, MS)
|
||||||
|
|
||||||
|
for AttrNode, Sc in Uses:
|
||||||
|
Var = AttrNode.value.id
|
||||||
|
Field = AttrNode.attr
|
||||||
|
Cls, DeclLine = Resolve(Sc, Var, AttrNode.lineno, AttrNode.col_offset)
|
||||||
|
if Cls is None:
|
||||||
|
UnresolvedCount += 1
|
||||||
|
continue
|
||||||
|
Fam, CName = Cls
|
||||||
|
if (Fam, CName) not in ClassFields:
|
||||||
|
UnresolvedCount += 1
|
||||||
|
continue
|
||||||
|
ResolvedCount += 1
|
||||||
|
if Field in Member(Fam, CName):
|
||||||
|
continue
|
||||||
|
Rel = os.path.relpath(Path, AppRoot)
|
||||||
|
SrcLine = RawLines[AttrNode.lineno - 1].strip()[:96] if AttrNode.lineno <= len(RawLines) else ''
|
||||||
|
Row = (Rel, AttrNode.lineno, Var, CName, Field, DeclLine, SrcLine, Fam)
|
||||||
|
if Field in FamilyFields.get(Fam, set()):
|
||||||
|
Other.append(Row)
|
||||||
|
else:
|
||||||
|
Nowhere.append(Row)
|
||||||
|
|
||||||
|
# 稳定排序(便于 diff / 门禁)
|
||||||
|
SortKey = lambda R: (R[0], R[1], R[2], R[4], R[7], R[3], R[5])
|
||||||
|
Nowhere.sort(key=SortKey)
|
||||||
|
Other.sort(key=SortKey)
|
||||||
|
|
||||||
|
BucketsN = {}
|
||||||
|
BucketsO = {}
|
||||||
|
for R in Nowhere:
|
||||||
|
Bump(BucketsN, BucketOf(R[7]))
|
||||||
|
for R in Other:
|
||||||
|
Bump(BucketsO, BucketOf(R[7]))
|
||||||
|
|
||||||
|
ParsedN = len(Files) - len(ParseFail)
|
||||||
|
UsesN = ResolvedCount + UnresolvedCount
|
||||||
|
ClassLine = ' '.join('%s=%d' % (f, sum(1 for (x, _n) in ClassFields if x == f))
|
||||||
|
for f in sorted(set(x for x, _n in ClassFields)))
|
||||||
|
|
||||||
|
def W(Fh, Rows, Tag):
|
||||||
|
for R in Rows:
|
||||||
|
Fh.write('%-46s:%-5d var=%s cls=%s.%s field=%s decl@%d 判定=%s %s\n' %
|
||||||
|
(R[0], R[1], R[2], R[7], R[3], R[4], R[5], Tag, R[6]))
|
||||||
|
|
||||||
|
OutDir = os.path.dirname(os.path.abspath(OutFile))
|
||||||
|
if OutDir:
|
||||||
|
os.makedirs(OutDir, exist_ok=True)
|
||||||
|
with open(OutFile, 'w', encoding='utf-8') as Fh:
|
||||||
|
Fh.write('files=%d parsed=%d parsefail=%d uses=%d NOWHERE=%d OTHER=%d RESOLVED=%d UNRESOLVED=%d\n' %
|
||||||
|
(len(Files), ParsedN, len(ParseFail), UsesN,
|
||||||
|
len(Nowhere), len(Other), ResolvedCount, UnresolvedCount))
|
||||||
|
Fh.write('classes: %s\n' % ClassLine)
|
||||||
|
Fh.write('NOWHERE buckets: %s\n' % sorted(BucketsN.items()))
|
||||||
|
Fh.write('OTHER buckets: %s\n' % sorted(BucketsO.items()))
|
||||||
|
if AmbiguousBases:
|
||||||
|
Fh.write('AMBIGUOUS BASES (跨族重名, 已不解析): %s\n' % sorted(AmbiguousBases))
|
||||||
|
Fh.write('\n=== NOWHERE (本族任何类都没有该字段) ===\n')
|
||||||
|
W(Fh, Nowhere, 'NOWHERE')
|
||||||
|
Fh.write('\n=== OTHER (字段只存在于本族别的类) ===\n')
|
||||||
|
W(Fh, Other, 'OTHER')
|
||||||
|
if ParseFail:
|
||||||
|
Fh.write('\n=== PARSE FAIL (vast 无法解析, 已跳过) ===\n')
|
||||||
|
for P in ParseFail:
|
||||||
|
Fh.write('%s:%s %s\n' % P)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'Files': len(Files), 'Parsed': ParsedN, 'ParseFail': ParseFail,
|
||||||
|
'Uses': UsesN, 'Nowhere': Nowhere, 'Other': Other,
|
||||||
|
'Resolved': ResolvedCount, 'Unresolved': UnresolvedCount,
|
||||||
|
'BucketsN': BucketsN, 'BucketsO': BucketsO,
|
||||||
|
'Ambiguous': sorted(AmbiguousBases), 'Classes': ClassLine,
|
||||||
|
'OutFile': OutFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def PrintSummary(Stats, Verbose):
|
||||||
|
print('files=%d parsed=%d parsefail=%d uses=%d NOWHERE=%d OTHER=%d RESOLVED=%d UNRESOLVED=%d -> %s' %
|
||||||
|
(Stats['Files'], Stats['Parsed'], len(Stats['ParseFail']), Stats['Uses'],
|
||||||
|
len(Stats['Nowhere']), len(Stats['Other']),
|
||||||
|
Stats['Resolved'], Stats['Unresolved'], Stats['OutFile']))
|
||||||
|
print('NOWHERE buckets: %s' % sorted(Stats['BucketsN'].items()))
|
||||||
|
print('OTHER buckets: %s' % sorted(Stats['BucketsO'].items()))
|
||||||
|
if Stats['Ambiguous']:
|
||||||
|
print('AMBIGUOUS BASES: %s' % Stats['Ambiguous'])
|
||||||
|
if Stats['ParseFail']:
|
||||||
|
print('PARSE FAIL: %s' % Stats['ParseFail'])
|
||||||
|
Limit = 25 if not Verbose else 100000
|
||||||
|
for R in Stats['Nowhere'][:Limit]:
|
||||||
|
print('N %-40s:%-5d %s.%s.%s [decl@%d]' % (R[0], R[1], R[7], R[3], R[4], R[5]))
|
||||||
|
for R in Stats['Other'][:Limit]:
|
||||||
|
print('O %-40s:%-5d %s.%s.%s [decl@%d]' % (R[0], R[1], R[7], R[3], R[4], R[5]))
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 5. --selftest: 内置合成用例(临时目录生成, 跑完即删)
|
||||||
|
# 断言"报出集合 == 预期集合";正例 6 条(含 vast 专有写法)、负例 4 个文件。
|
||||||
|
# ============================================================
|
||||||
|
SelftestCases = [
|
||||||
|
('CaseP1AstNameOther.vp', """import ast
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeP1(x_r: rawptr[ast.AST]) -> int:
|
||||||
|
return x_r.name
|
||||||
|
"""),
|
||||||
|
('CaseP2LlvFunctionTyOther.vp', """import llvmlite
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeP2(x_r: rawptr[llvmlite.Function]) -> int:
|
||||||
|
return x_r.Ty
|
||||||
|
"""),
|
||||||
|
('CaseP3AstNowhere.vp', """import ast
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeP3(x_r: rawptr[ast.AST]) -> int:
|
||||||
|
return x_r.zzz_not_a_field
|
||||||
|
"""),
|
||||||
|
('CaseP4AstStarOther.vp', """import ast
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeP4(x_r: rawptr[ast.AST]*) -> int:
|
||||||
|
return x_r.name
|
||||||
|
"""),
|
||||||
|
('CaseP5AstStarStarOther.vp', """import ast
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeP5(x_r: rawptr[ast.AST]**) -> int:
|
||||||
|
return x_r.name
|
||||||
|
"""),
|
||||||
|
('CaseP6ReturnInferOther.vp', """import llvmlite
|
||||||
|
|
||||||
|
|
||||||
|
def MkP6(x_r: rawptr[llvmlite.Function]) -> rawptr[llvmlite.Function]:
|
||||||
|
return x_r
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeP6(x_r: rawptr[llvmlite.Function]) -> int:
|
||||||
|
y_r = MkP6(x_r)
|
||||||
|
return y_r.Ty
|
||||||
|
"""),
|
||||||
|
('CaseN1ParamNodeTy.vp', """import llvmlite
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeN1(x_r: rawptr[llvmlite.ParamNode]) -> int:
|
||||||
|
return x_r.Ty
|
||||||
|
"""),
|
||||||
|
('CaseN2SameNameScopes.vp', """import llvmlite
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeN2A(x_r: rawptr[llvmlite.ParamNode]) -> int:
|
||||||
|
return x_r.Ty
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeN2B(x_r: rawptr[llvmlite.Function]) -> int:
|
||||||
|
return x_r.Ty
|
||||||
|
"""),
|
||||||
|
('CaseN3DocComment.vp', """import ast
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeN3(x_r: rawptr[ast.AST]) -> int:
|
||||||
|
\"\"\"docstring: x_r.name / x_r.zzz_not_a_field 只是说明文字, 不是代码。\"\"\"
|
||||||
|
# 注释: x_r.name / x_r.zzz_not_a_field
|
||||||
|
return 0
|
||||||
|
"""),
|
||||||
|
('CaseN4ReturnInferOk.vp', """import llvmlite
|
||||||
|
|
||||||
|
|
||||||
|
def MkN4(x_r: rawptr[llvmlite.ParamNode]) -> rawptr[llvmlite.ParamNode]:
|
||||||
|
return x_r
|
||||||
|
|
||||||
|
|
||||||
|
def ProbeN4(x_r: rawptr[llvmlite.ParamNode]) -> int:
|
||||||
|
y_r = MkN4(x_r)
|
||||||
|
return y_r.Ty
|
||||||
|
"""),
|
||||||
|
]
|
||||||
|
|
||||||
|
# (文件, 变量, 族.类, 字段) -> 判定
|
||||||
|
SelftestExpected = {
|
||||||
|
('CaseP1AstNameOther.vp', 'x_r', 'ast.AST', 'name'): 'OTHER',
|
||||||
|
('CaseP2LlvFunctionTyOther.vp', 'x_r', 'llvmlite.Function', 'Ty'): 'OTHER',
|
||||||
|
('CaseP3AstNowhere.vp', 'x_r', 'ast.AST', 'zzz_not_a_field'): 'NOWHERE',
|
||||||
|
('CaseP4AstStarOther.vp', 'x_r', 'ast.AST', 'name'): 'OTHER',
|
||||||
|
('CaseP5AstStarStarOther.vp', 'x_r', 'ast.AST', 'name'): 'OTHER',
|
||||||
|
('CaseP6ReturnInferOther.vp', 'y_r', 'llvmlite.Function', 'Ty'): 'OTHER',
|
||||||
|
('CaseN2SameNameScopes.vp', 'x_r', 'llvmlite.Function', 'Ty'): 'OTHER',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def SelfTest(IncludesDir, OutFile):
|
||||||
|
AppRoot = tempfile.mkdtemp(prefix='ScanAstFieldMissSelftest.')
|
||||||
|
try:
|
||||||
|
for Name, Src in SelftestCases:
|
||||||
|
with open(os.path.join(AppRoot, Name), 'w', encoding='utf-8', newline='\n') as Fh:
|
||||||
|
Fh.write(Src)
|
||||||
|
Report = OutFile or os.path.join(AppRoot, 'Selftest.txt')
|
||||||
|
Stats = RunScan(AppRoot, IncludesDir, Report, False)
|
||||||
|
|
||||||
|
Reported = {}
|
||||||
|
for R in Stats['Nowhere']:
|
||||||
|
Reported[(R[0], R[2], '%s.%s' % (R[7], R[3]), R[4])] = 'NOWHERE'
|
||||||
|
for R in Stats['Other']:
|
||||||
|
Reported[(R[0], R[2], '%s.%s' % (R[7], R[3]), R[4])] = 'OTHER'
|
||||||
|
|
||||||
|
Ok = True
|
||||||
|
if Stats['ParseFail']:
|
||||||
|
Ok = False
|
||||||
|
print('[selftest] 解析失败(预期 parsefail=0):')
|
||||||
|
for P in Stats['ParseFail']:
|
||||||
|
print(' %s:%s %s' % P)
|
||||||
|
Missing = sorted(set(SelftestExpected) - set(Reported))
|
||||||
|
Extra = sorted(set(Reported) - set(SelftestExpected))
|
||||||
|
TagDiff = sorted(K for K in set(Reported) & set(SelftestExpected)
|
||||||
|
if Reported[K] != SelftestExpected[K])
|
||||||
|
for K in Missing:
|
||||||
|
Ok = False
|
||||||
|
print('[selftest] 漏报: %s -> 预期 %s' % (K, SelftestExpected[K]))
|
||||||
|
for K in Extra:
|
||||||
|
Ok = False
|
||||||
|
print('[selftest] 误报: %s -> %s' % (K, Reported[K]))
|
||||||
|
for K in TagDiff:
|
||||||
|
Ok = False
|
||||||
|
print('[selftest] 判定不符: %s -> %s(预期 %s)' % (K, Reported[K], SelftestExpected[K]))
|
||||||
|
|
||||||
|
print('[selftest] files=%d parsed=%d parsefail=%d NOWHERE=%d OTHER=%d 报出=%d 预期=%d' %
|
||||||
|
(Stats['Files'], Stats['Parsed'], len(Stats['ParseFail']),
|
||||||
|
len(Stats['Nowhere']), len(Stats['Other']), len(Reported), len(SelftestExpected)))
|
||||||
|
print('[selftest] %s' % ('PASS' if Ok else 'FAIL'))
|
||||||
|
return 0 if Ok else 1
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(AppRoot, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 6. CLI
|
||||||
|
# ============================================================
|
||||||
|
def ParseArgs(Argv):
|
||||||
|
Opt = {'AppRoot': DefaultAppRoot, 'Includes': DefaultIncludesDir, 'Out': None,
|
||||||
|
'Strict': False, 'Verbose': False, 'SelfTest': False}
|
||||||
|
Need = {'--app-root': 'AppRoot', '--includes': 'Includes', '--out': 'Out'}
|
||||||
|
Flags = {'--strict': 'Strict', '--verbose': 'Verbose', '--selftest': 'SelfTest'}
|
||||||
|
I = 0
|
||||||
|
while I < len(Argv):
|
||||||
|
A = Argv[I]
|
||||||
|
if A in Need:
|
||||||
|
if I + 1 >= len(Argv):
|
||||||
|
sys.stderr.write('参数缺值: %s\n' % A)
|
||||||
|
return None
|
||||||
|
Opt[Need[A]] = Argv[I + 1]
|
||||||
|
I += 2
|
||||||
|
continue
|
||||||
|
if A in Flags:
|
||||||
|
Opt[Flags[A]] = True
|
||||||
|
I += 1
|
||||||
|
continue
|
||||||
|
sys.stderr.write('未知参数: %s(用法见本脚本 docstring)\n' % A)
|
||||||
|
return None
|
||||||
|
return Opt
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
Opt = ParseArgs(sys.argv[1:])
|
||||||
|
if Opt is None:
|
||||||
|
return 2
|
||||||
|
if Opt['SelfTest']:
|
||||||
|
return SelfTest(Opt['Includes'], Opt['Out'])
|
||||||
|
Stats = RunScan(Opt['AppRoot'], Opt['Includes'], Opt['Out'] or DefaultOutFile, Opt['Verbose'])
|
||||||
|
PrintSummary(Stats, Opt['Verbose'])
|
||||||
|
if Opt['Strict'] and (Stats['Nowhere'] or Stats['Other'] or Stats['ParseFail']):
|
||||||
|
sys.stderr.write('[strict] 门禁失败: NOWHERE=%d OTHER=%d parsefail=%d\n' %
|
||||||
|
(len(Stats['Nowhere']), len(Stats['Other']), len(Stats['ParseFail'])))
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -149,7 +149,7 @@ def main() -> None:
|
|||||||
parser.add_argument('--clear-cache', action='store_true', help='清除 .transpyc_cache 全局缓存(.vpi/.stub.ll/.doc.json)')
|
parser.add_argument('--clear-cache', action='store_true', help='清除 .transpyc_cache 全局缓存(.vpi/.stub.ll/.doc.json)')
|
||||||
parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)')
|
parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)')
|
||||||
parser.add_argument('--dwarf', action='store_true', help='函数级 DWARF 调试元数据:每模块一份 DICompileUnit/DIFile,每个 define 一份 DISubprogram 并挂 !dbg,函数入口第一条指令挂 DILocation。使 ASan/llvm-symbolizer 能把崩溃地址反查为 "<文件>.vp:函数起始行"(默认关闭;--asan 时自动开启)。开启时强制重译 App 模块(不复用不含 !llvm.dbg.cu 的旧 .ll/.obj),.ll/.obj 体积与编译时间略增')
|
parser.add_argument('--dwarf', action='store_true', help='函数级 DWARF 调试元数据:每模块一份 DICompileUnit/DIFile,每个 define 一份 DISubprogram 并挂 !dbg,函数入口第一条指令挂 DILocation。使 ASan/llvm-symbolizer 能把崩溃地址反查为 "<文件>.vp:函数起始行"(默认关闭;--asan 时自动开启)。开启时强制重译 App 模块(不复用不含 !llvm.dbg.cu 的旧 .ll/.obj),.ll/.obj 体积与编译时间略增')
|
||||||
parser.add_argument('--ctrlc-stack', action='store_true', help='Ctrl+C 调用栈打印(仅 Windows/PE):链接完成后用 llvm-dwarfdump 把最终 exe 的 .debug_line/.debug_info 导出为 "<exe>.stackmap" 地址→源行旁文件(小端定长格式),供 exe 内运行时在 Ctrl+C/Ctrl+Break 时把 CRT 到当前的返回地址逐帧翻译为 "<文件>.vp:行/函数"。会自动开启 DWARF(同 --asan,否则行表为空);找不到 llvm-dwarfdump / 调用失败 / 解析不到任何行记录 / 目标不是 Windows PE 时直接报错终止编译')
|
parser.add_argument('--traceback', action='store_true', help='Ctrl+C 调用栈打印(仅 Windows/PE):链接完成后用 llvm-dwarfdump 把最终 exe 的 .debug_line/.debug_info 导出为 "<exe>.stackmap" 地址→源行旁文件(小端定长格式),供 exe 内运行时在 Ctrl+C/Ctrl+Break 时把 CRT 到当前的返回地址逐帧翻译为 "<文件>.vp:行/函数"。会自动开启 DWARF(同 --asan,否则行表为空);找不到 llvm-dwarfdump / 调用失败 / 解析不到任何行记录 / 目标不是 Windows PE 时直接报错终止编译')
|
||||||
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)')
|
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)')
|
||||||
parser.add_argument('--hotspot-sites', nargs='?', const='', default=None, help='调用点热点统计:为每个匹配白名单的直接 call 点插入计数器,运行时 Ctrl+C 各模块按真实次数降序写入 <path>(默认为 hotspot_sites.txt);同时为每个函数定义加计时器,按自身耗时(self)降序写入同目录 hotspot_time.txt,行为 `<函数名> <self> <inc> <进入次数>`(单位 tick,x86_64 上为序列化 lfence; rdtsc);self 已扣除被插桩子调用的 inc,但不含未插桩被调方(间接调用、strcmp/memcpy 等无定义外部函数),它们计入调用者的 self')
|
parser.add_argument('--hotspot-sites', nargs='?', const='', default=None, help='调用点热点统计:为每个匹配白名单的直接 call 点插入计数器,运行时 Ctrl+C 各模块按真实次数降序写入 <path>(默认为 hotspot_sites.txt);同时为每个函数定义加计时器,按自身耗时(self)降序写入同目录 hotspot_time.txt,行为 `<函数名> <self> <inc> <进入次数>`(单位 tick,x86_64 上为序列化 lfence; rdtsc);self 已扣除被插桩子调用的 inc,但不含未插桩被调方(间接调用、strcmp/memcpy 等无定义外部函数),它们计入调用者的 self')
|
||||||
parser.add_argument('--hotspot-callees', default=None, help='--hotspot-sites 的白名单被调函数基名(逗号分隔),默认内存/字符串族 strcmp,strncmp,strchr,strlen,strstr,memcpy,memset,memmove,strcpy,strcat')
|
parser.add_argument('--hotspot-callees', default=None, help='--hotspot-sites 的白名单被调函数基名(逗号分隔),默认内存/字符串族 strcmp,strncmp,strchr,strlen,strstr,memcpy,memset,memmove,strcpy,strcat')
|
||||||
@@ -163,20 +163,20 @@ def main() -> None:
|
|||||||
if args.asan:
|
if args.asan:
|
||||||
_vlog().info('ASan 模式已启用(opt -passes=asan + clang64 libclang_rt.asan_dynamic*)', 'asan')
|
_vlog().info('ASan 模式已启用(opt -passes=asan + clang64 libclang_rt.asan_dynamic*)', 'asan')
|
||||||
|
|
||||||
# Ctrl+C 调用栈(--ctrlc-stack):链接后用 llvm-dwarfdump 导出 <exe>.stackmap。
|
# Ctrl+C 调用栈(--traceback):链接后用 llvm-dwarfdump 导出 <exe>.stackmap。
|
||||||
# 不在此处做工具/平台校验——真正的 fail-fast 落在链接成功后的旁文件导出步骤
|
# 不在此处做工具/平台校验——真正的 fail-fast 落在链接成功后的旁文件导出步骤
|
||||||
# (那里才有最终 exe 路径可判 PE)。此处仅提示已启用。
|
# (那里才有最终 exe 路径可判 PE)。此处仅提示已启用。
|
||||||
if args.ctrlc_stack:
|
if args.traceback:
|
||||||
_vlog().info('Ctrl+C 调用栈模式已启用(链接后用 llvm-dwarfdump 导出 <exe>.stackmap)', 'ctrlc-stack')
|
_vlog().info('Ctrl+C 调用栈模式已启用(链接后用 llvm-dwarfdump 导出 <exe>.stackmap)', 'traceback')
|
||||||
|
|
||||||
# 函数级 DWARF 调试元数据(--dwarf):默认关闭(硬要求——普通构建不承担
|
# 函数级 DWARF 调试元数据(--dwarf):默认关闭(硬要求——普通构建不承担
|
||||||
# 任何风险,生成的 .ll 与改动前逐字节一致)。--asan 时自动开启:ASan 报告
|
# 任何风险,生成的 .ll 与改动前逐字节一致)。--asan 时自动开启:ASan 报告
|
||||||
# 本身就需要行号才能定位,且 ASan 构建已是显式选择的非默认路径。
|
# 本身就需要行号才能定位,且 ASan 构建已是显式选择的非默认路径。
|
||||||
# --ctrlc-stack 同理自动开启:旁文件的"地址→源行"完全来自 DWARF 行表,
|
# --traceback 同理自动开启:旁文件的"地址→源行"完全来自 DWARF 行表,
|
||||||
# 没有 DI 元数据时 .debug_line 为空、旁文件必为空。
|
# 没有 DI 元数据时 .debug_line 为空、旁文件必为空。
|
||||||
dwarf_enabled: bool = bool(args.dwarf or args.asan or args.ctrlc_stack)
|
dwarf_enabled: bool = bool(args.dwarf or args.asan or args.traceback)
|
||||||
if dwarf_enabled:
|
if dwarf_enabled:
|
||||||
_auto_src: str = '--asan' if args.asan else ('--ctrlc-stack' if args.ctrlc_stack else '')
|
_auto_src: str = '--asan' if args.asan else ('--traceback' if args.traceback else '')
|
||||||
_vlog().info(
|
_vlog().info(
|
||||||
'函数级 DWARF 调试元数据已启用:每个 .vp 模块一份 DICompileUnit + 每函数 DISubprogram/DILocation'
|
'函数级 DWARF 调试元数据已启用:每个 .vp 模块一份 DICompileUnit + 每函数 DISubprogram/DILocation'
|
||||||
+ (f'(由 {_auto_src} 自动开启)' if _auto_src and not args.dwarf else ''),
|
+ (f'(由 {_auto_src} 自动开启)' if _auto_src and not args.dwarf else ''),
|
||||||
@@ -349,7 +349,7 @@ def main() -> None:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
_vlog().info(f"includes.binary: 已清空 {precompiled_dir} ({cleared} 个文件)")
|
_vlog().info(f"includes.binary: 已清空 {precompiled_dir} ({cleared} 个文件)")
|
||||||
trans: Phase2Translator = Phase2Translator(src, temp_dir, output_dir, compile_cmd=cc_cmd, compile_flags=cc_flags, linker_cmd=linker_cmd, linker_flags=linker_flags, linker_output=linker_output, static_libs=static_libs, dynamic_libs=dynamic_libs, include_dirs=project_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout, force_recompile_includes=force_recompile_includes, asan_mode=args.asan, dwarf_enabled=dwarf_enabled, ctrlc_stack_enabled=args.ctrlc_stack)
|
trans: Phase2Translator = Phase2Translator(src, temp_dir, output_dir, compile_cmd=cc_cmd, compile_flags=cc_flags, linker_cmd=linker_cmd, linker_flags=linker_flags, linker_output=linker_output, static_libs=static_libs, dynamic_libs=dynamic_libs, include_dirs=project_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout, force_recompile_includes=force_recompile_includes, asan_mode=args.asan, dwarf_enabled=dwarf_enabled, traceback_enabled=args.traceback)
|
||||||
trans.sha1_map = Load_sha1_map(temp_dir)
|
trans.sha1_map = Load_sha1_map(temp_dir)
|
||||||
trans.slice_level = slice_level
|
trans.slice_level = slice_level
|
||||||
# 调用点热点统计与每函数计时(--hotspot-sites):None=未启用(零影响)
|
# 调用点热点统计与每函数计时(--hotspot-sites):None=未启用(零影响)
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ from lib.includes import t
|
|||||||
from lib.includes.t import CTypeRegistry
|
from lib.includes.t import CTypeRegistry
|
||||||
from lib.core.SymbolUtils import (
|
from lib.core.SymbolUtils import (
|
||||||
IsListAnnotation, AnnotationContainsName, IsCPtrSubscriptNode, IsCPtrNode,
|
IsListAnnotation, AnnotationContainsName, IsCPtrSubscriptNode, IsCPtrNode,
|
||||||
IsRawPtrSubscriptNode, IsPtrSubscriptNode, ResolveGenericTypeArgStr)
|
IsRawPtrSubscriptNode, IsPtrSubscriptNode, ResolveGenericTypeArgStr,
|
||||||
|
EnumClassIntWidth, ClassHasFuncLevelCVTable)
|
||||||
|
|
||||||
|
|
||||||
class DeclarationGenerator:
|
class DeclarationGenerator:
|
||||||
@@ -55,7 +56,9 @@ class DeclarationGenerator:
|
|||||||
t.configure_platform(self.target_triple)
|
t.configure_platform(self.target_triple)
|
||||||
|
|
||||||
def generate(self, pyi_content: str, src_path: str) -> str:
|
def generate(self, pyi_content: str, src_path: str) -> str:
|
||||||
tree: ast.Module = ast.parse(pyi_content)
|
# 传 src_path:解析失败时报错/回退诊断才带得上来源(否则 vast 记为 <unknown>,
|
||||||
|
# 既无法定位文件,也无法知道是哪份 .pyi 出问题)
|
||||||
|
tree: ast.Module = ast.parse(pyi_content, src_path)
|
||||||
|
|
||||||
self._DefineConstants: dict[str, int | str] = {}
|
self._DefineConstants: dict[str, int | str] = {}
|
||||||
self._pyi_tree = tree
|
self._pyi_tree = tree
|
||||||
@@ -255,9 +258,17 @@ class DeclarationGenerator:
|
|||||||
if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable':
|
if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable':
|
||||||
return None
|
return None
|
||||||
VarType: str = self._get_type_str(node.annotation)
|
VarType: str = self._get_type_str(node.annotation)
|
||||||
if VarType.startswith('[0 x ') and node.value and isinstance(node.value, ast.List):
|
if VarType.startswith('[0 x ') and node.value:
|
||||||
elem_type: str = VarType[5:-1]
|
elem_type: str = VarType[5:-1]
|
||||||
actual_count: int = len(node.value.elts)
|
actual_count: int = 0
|
||||||
|
if isinstance(node.value, ast.List):
|
||||||
|
actual_count = len(node.value.elts)
|
||||||
|
elif isinstance(node.value, ast.Constant) and isinstance(node.value.value, str):
|
||||||
|
# 字符串字面量:与 HandlesBase.ParseArrayCount(:731-732) / HandlesAssign
|
||||||
|
# 全局路径同口径(len(str)+1,含结尾 NUL)。此前只修 List 值 → CArray
|
||||||
|
# 字符串全局的 stub 声明停留在 [0 x i8],与定义端 [N x i8] 长度不一致
|
||||||
|
# (跨模块 type mismatch / llc 拒绝)。
|
||||||
|
actual_count = len(node.value.value) + 1
|
||||||
if actual_count > 0:
|
if actual_count > 0:
|
||||||
VarType = f'[{actual_count} x {elem_type}]'
|
VarType = f'[{actual_count} x {elem_type}]'
|
||||||
return f'@{var_name} = external global {VarType}'
|
return f'@{var_name} = external global {VarType}'
|
||||||
@@ -350,6 +361,10 @@ class DeclarationGenerator:
|
|||||||
if is_enum:
|
if is_enum:
|
||||||
enum_values: dict[str, int] = {}
|
enum_values: dict[str, int] = {}
|
||||||
next_value: int = 0
|
next_value: int = 0
|
||||||
|
# 枚举存储宽度 = 最宽成员类型(与定义端 HandlesClassDef / 跨模块读取端
|
||||||
|
# 同一规则源 EnumClassIntWidth;此处无 SymbolTable,走 t 类名 / builtins /
|
||||||
|
# 库 typedef 的表)。声明端的全局类型必须与定义端运行时常量同宽(ABI)。
|
||||||
|
enum_width: int = EnumClassIntWidth(node.body)
|
||||||
for item in node.body:
|
for item in node.body:
|
||||||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||||||
var_name: str = item.target.id
|
var_name: str = item.target.id
|
||||||
@@ -367,7 +382,7 @@ class DeclarationGenerator:
|
|||||||
enum_values[var_name] = next_value
|
enum_values[var_name] = next_value
|
||||||
next_value += 1
|
next_value += 1
|
||||||
for var_name, value in enum_values.items():
|
for var_name, value in enum_values.items():
|
||||||
decls.append(f'@__config_{class_name}_{var_name} = external global i32')
|
decls.append(f'@__config_{class_name}_{var_name} = external global i{enum_width}')
|
||||||
return decls
|
return decls
|
||||||
if is_renum:
|
if is_renum:
|
||||||
# REnum 布局: { i32 __tag, <max_variant_payload> }
|
# REnum 布局: { i32 __tag, <max_variant_payload> }
|
||||||
@@ -473,6 +488,14 @@ class DeclarationGenerator:
|
|||||||
# (@t.NoVTable 标记的类及其子类不使用 vtable,字段展平嵌入)
|
# (@t.NoVTable 标记的类及其子类不使用 vtable,字段展平嵌入)
|
||||||
if has_parent_class and not is_cvtable and not is_novtable and not parent_is_novtable:
|
if has_parent_class and not is_cvtable and not is_novtable and not parent_is_novtable:
|
||||||
is_cvtable = True
|
is_cvtable = True
|
||||||
|
# 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable:
|
||||||
|
# 与 HandlesClassDef._resolve_class_flags / TPV _detect_vtable_status 的
|
||||||
|
# `_has_any_func_cvtable` 分支逐条对齐(类级 NoVTable / 无真实父类)。
|
||||||
|
# 必须在此同步:定义模块按 IsCVTable 在 struct 首部放 vptr,若 stub 不同步,
|
||||||
|
# 跨模块使用该类的 struct 布局会比定义模块少一个字段 ⇒ 字段整体错位。
|
||||||
|
if not is_cvtable and ClassHasFuncLevelCVTable(node):
|
||||||
|
if is_novtable or not has_parent_class:
|
||||||
|
is_cvtable = True
|
||||||
base_has_vtable: bool = False
|
base_has_vtable: bool = False
|
||||||
for base in node.bases:
|
for base in node.bases:
|
||||||
base_name: str | None = None
|
base_name: str | None = None
|
||||||
@@ -847,6 +870,7 @@ class DeclarationGenerator:
|
|||||||
if has_methods and not is_cpython_object:
|
if has_methods and not is_cpython_object:
|
||||||
is_cpython_object = True
|
is_cpython_object = True
|
||||||
base_has_vtable: bool = False
|
base_has_vtable: bool = False
|
||||||
|
has_real_parent: bool = False
|
||||||
for base in node.bases:
|
for base in node.bases:
|
||||||
base_name: str | None = None
|
base_name: str | None = None
|
||||||
if isinstance(base, ast.Attribute):
|
if isinstance(base, ast.Attribute):
|
||||||
@@ -859,6 +883,7 @@ class DeclarationGenerator:
|
|||||||
elif isinstance(base.value, ast.Name):
|
elif isinstance(base.value, ast.Name):
|
||||||
base_name = base.value.id
|
base_name = base.value.id
|
||||||
if base_name and not self._is_marker_base(base_name):
|
if base_name and not self._is_marker_base(base_name):
|
||||||
|
has_real_parent = True
|
||||||
_base_node: ast.ClassDef | None = None
|
_base_node: ast.ClassDef | None = None
|
||||||
for n in ast.iter_child_nodes(self._pyi_tree):
|
for n in ast.iter_child_nodes(self._pyi_tree):
|
||||||
if isinstance(n, ast.ClassDef) and n.name == base_name:
|
if isinstance(n, ast.ClassDef) and n.name == base_name:
|
||||||
@@ -874,6 +899,11 @@ class DeclarationGenerator:
|
|||||||
base_has_vtable = True
|
base_has_vtable = True
|
||||||
if base_has_vtable:
|
if base_has_vtable:
|
||||||
break
|
break
|
||||||
|
# 方法级 virtual 触发类级 vtable(与上方 _generate_class_decl 同一判据,
|
||||||
|
# 保持在同一个类里两处口径一致;详见该处注释)
|
||||||
|
if not is_cvtable and not base_has_vtable and ClassHasFuncLevelCVTable(node):
|
||||||
|
if is_novtable or not has_real_parent:
|
||||||
|
is_cvtable = True
|
||||||
return has_methods and is_cvtable and not base_has_vtable and not is_novtable
|
return has_methods and is_cvtable and not base_has_vtable and not is_novtable
|
||||||
|
|
||||||
def _FlattenInheritedDecl(self, cls_name: str, seen_cls: set, member_types: list[str],
|
def _FlattenInheritedDecl(self, cls_name: str, seen_cls: set, member_types: list[str],
|
||||||
@@ -1156,7 +1186,7 @@ class DeclarationGenerator:
|
|||||||
return _dg_llvm
|
return _dg_llvm
|
||||||
_dg_node: ast.AST | None = None
|
_dg_node: ast.AST | None = None
|
||||||
try:
|
try:
|
||||||
_dg_node = ast.parse(_dg_bv, mode='eval').body
|
_dg_node = ast.parse(_dg_bv, '<DeclarationGenerator:type_expr>', mode='eval').body
|
||||||
except Exception:
|
except Exception:
|
||||||
_dg_node = None
|
_dg_node = None
|
||||||
if _dg_node is not None:
|
if _dg_node is not None:
|
||||||
@@ -1291,11 +1321,13 @@ class DeclarationGenerator:
|
|||||||
return f'[{self._get_const_int(annotation.slice)} x i8]'
|
return f'[{self._get_const_int(annotation.slice)} x i8]'
|
||||||
if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int):
|
if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int):
|
||||||
return f'[{annotation.slice.value} x {base}]'
|
return f'[{annotation.slice.value} x {base}]'
|
||||||
# 处理 t.CArray[elem_type, count] / rawarray[elem_type, count] 注解 → [count x elem_type]
|
# 处理 t.CArray[elem_type, count] / rawarray[elem_type, count] / array[elem_type, count]
|
||||||
# 支持 t.CArray (ast.Attribute)、CArray/rawarray (ast.Name) 三种写法
|
# 注解 → [count x elem_type]
|
||||||
|
# 支持 t.CArray (ast.Attribute)、CArray/rawarray/array (ast.Name) 三种写法
|
||||||
|
# (array 为本轮新增的内建软关键字,与 t.CArray 逐点等价)
|
||||||
ValueIsCArray: bool = (
|
ValueIsCArray: bool = (
|
||||||
(isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CArray')
|
(isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CArray')
|
||||||
or (isinstance(annotation.value, ast.Name) and annotation.value.id in ('CArray', 'rawarray'))
|
or (isinstance(annotation.value, ast.Name) and annotation.value.id in ('CArray', 'rawarray', 'array'))
|
||||||
)
|
)
|
||||||
if ValueIsCArray:
|
if ValueIsCArray:
|
||||||
SliceNode: ast.AST = annotation.slice
|
SliceNode: ast.AST = annotation.slice
|
||||||
@@ -1307,9 +1339,13 @@ class DeclarationGenerator:
|
|||||||
# count 为 None 或 0 → 零长度数组(外部符号)
|
# count 为 None 或 0 → 零长度数组(外部符号)
|
||||||
return f'[0 x {ElemType}]'
|
return f'[0 x {ElemType}]'
|
||||||
elif isinstance(SliceNode, (ast.Attribute, ast.Name, ast.Subscript)):
|
elif isinstance(SliceNode, (ast.Attribute, ast.Name, ast.Subscript)):
|
||||||
# 单参数 t.CArray[elem_type] → 指针模式
|
# 单参数 t.CArray[elem_type]:与 t.CArray[elem_type, None] 语义统一为
|
||||||
|
# "长度自动推导数组"。此处无初始化字面量可推导 → 与 ,None 分支同用
|
||||||
|
# [0 x elem] 占位,真实长度由 _generate_global_decl 按初始化字面量
|
||||||
|
# (List 长度 / str 长度+1) 修正。
|
||||||
|
# 参数位置经 _decay_array_to_ptr(:23-28) 退化为 elem*,与旧指针行为一致。
|
||||||
ElemType: str = self._get_type_str(SliceNode, embedded=True)
|
ElemType: str = self._get_type_str(SliceNode, embedded=True)
|
||||||
return f'{ElemType}*'
|
return f'[0 x {ElemType}]'
|
||||||
if isinstance(annotation.value, ast.Name) and annotation.value.id == 'tuple':
|
if isinstance(annotation.value, ast.Name) and annotation.value.id == 'tuple':
|
||||||
slice_node: ast.AST = annotation.slice
|
slice_node: ast.AST = annotation.slice
|
||||||
elem_types: list[str]
|
elem_types: list[str]
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import traceback
|
|||||||
from lib.Projectrans.Utils import compute_sha1, get_file_dependencies, find_reachable_files_from_entries, parse_python_file
|
from lib.Projectrans.Utils import compute_sha1, get_file_dependencies, find_reachable_files_from_entries, parse_python_file
|
||||||
from lib.Projectrans.DeclarationGenerator import DeclarationGenerator
|
from lib.Projectrans.DeclarationGenerator import DeclarationGenerator
|
||||||
from lib.core.VLogger import get_logger as _vlog
|
from lib.core.VLogger import get_logger as _vlog
|
||||||
|
from lib.core.SymbolUtils import ExpandSoftKeywords
|
||||||
from lib.constants.config import mode as _ConfigMode
|
from lib.constants.config import mode as _ConfigMode
|
||||||
from lib.includes import t
|
from lib.includes import t
|
||||||
from StubGen import PythonToStubConverter
|
from StubGen import PythonToStubConverter
|
||||||
@@ -383,6 +384,11 @@ class Phase1Generator:
|
|||||||
|
|
||||||
用于 .stub.ll 缓存依赖校验:当依赖的 include SHA1 变化时,
|
用于 .stub.ll 缓存依赖校验:当依赖的 include SHA1 变化时,
|
||||||
当前 include 的 .stub.ll 缓存应失效(因为类型引用的 SHA1 前缀会变化)。
|
当前 include 的 .stub.ll 缓存应失效(因为类型引用的 SHA1 前缀会变化)。
|
||||||
|
|
||||||
|
签名同时纳入 includes/builtins.vp:它是注解类型解析的隐式输入
|
||||||
|
(不在任何模块的 import 闭包,但其 CTypedef 参与签名/字段类型解析),
|
||||||
|
其内容变化必须让所有 .stub.ll 失效——否则旧声明(如 sizet 曾被解析成
|
||||||
|
%"{mod}.sizet"* 假结构体)会被缓存继续引用,源码修复不生效。
|
||||||
"""
|
"""
|
||||||
if not self._include_dep_graph or not self._include_file_map:
|
if not self._include_dep_graph or not self._include_file_map:
|
||||||
return ''
|
return ''
|
||||||
@@ -400,6 +406,11 @@ class Phase1Generator:
|
|||||||
if DepSha1:
|
if DepSha1:
|
||||||
Result.add(DepSha1)
|
Result.add(DepSha1)
|
||||||
Queue.extend(self._include_dep_graph.get(DepMod, set()))
|
Queue.extend(self._include_dep_graph.get(DepMod, set()))
|
||||||
|
BuiltinsInfo: tuple[str, str, str] | None = self._include_file_map.get('builtins')
|
||||||
|
if BuiltinsInfo is not None:
|
||||||
|
BuiltinsSha1: str = self._get_sha1(BuiltinsInfo[0])
|
||||||
|
if BuiltinsSha1:
|
||||||
|
Result.add(BuiltinsSha1)
|
||||||
return '|'.join(sorted(Result))
|
return '|'.join(sorted(Result))
|
||||||
|
|
||||||
def _ReadStubDep(self, stub_path: str) -> str:
|
def _ReadStubDep(self, stub_path: str) -> str:
|
||||||
@@ -510,7 +521,9 @@ class Phase1Generator:
|
|||||||
"""
|
"""
|
||||||
result: dict[str, str] = {}
|
result: dict[str, str] = {}
|
||||||
try:
|
try:
|
||||||
tree = ast.parse(content)
|
# `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析,
|
||||||
|
# 否则 SyntaxError 被吞成"无 docstring",与主翻译路径口径不一致。
|
||||||
|
tree = ast.parse(ExpandSoftKeywords(content), '<Phase1Generator:_extract_docstrings>')
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
return result
|
return result
|
||||||
for node in tree.body:
|
for node in tree.body:
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ from lib.core.LLVMCG.IRMetadataFix import SanitizeIRMetadata
|
|||||||
_FILE_STAT_CACHE: dict[str, bool] = {}
|
_FILE_STAT_CACHE: dict[str, bool] = {}
|
||||||
|
|
||||||
|
|
||||||
# --ctrlc-stack(Ctrl+C 调用栈)强制纳入编译+链接的 include 模块源文件名。
|
# --traceback(Ctrl+C 调用栈)强制纳入编译+链接的 include 模块源文件名。
|
||||||
# 该模块对外只有 Install() 入口,且无任何用户代码 import 它 —— 若不强制纳入,
|
# 该模块对外只有 Install() 入口,且无任何用户代码 import 它 —— 若不强制纳入,
|
||||||
# 产物里既没有 Install 符号、也没有调用它的启动期构造器,功能静默失效。
|
# 产物里既没有 Install 符号、也没有调用它的启动期构造器,功能静默失效。
|
||||||
# .vp 优先,.py 仅为向后兼容的回退名。
|
# .vp 优先,.py 仅为向后兼容的回退名。
|
||||||
_CTRLC_STACK_MODULE_FILES: tuple[str, ...] = ('dbgstack.vp', 'dbgstack.py')
|
_TRACEBACK_MODULE_FILES: tuple[str, ...] = ('traceback.vp', 'traceback.py')
|
||||||
|
|
||||||
|
|
||||||
def _CachedIsFile(path: str) -> bool:
|
def _CachedIsFile(path: str) -> bool:
|
||||||
@@ -80,9 +80,9 @@ def _FindAsanRuntimeDir() -> str | None:
|
|||||||
return found[-1]
|
return found[-1]
|
||||||
|
|
||||||
|
|
||||||
# ---------- --ctrlc-stack:地址→源行 旁文件(<exe>.stackmap)----------
|
# ---------- --traceback:地址→源行 旁文件(<exe>.stackmap)----------
|
||||||
#
|
#
|
||||||
# 开启 --ctrlc-stack 时,链接成功后用 llvm-dwarfdump 解析最终 PE 的
|
# 开启 --traceback 时,链接成功后用 llvm-dwarfdump 解析最终 PE 的
|
||||||
# .debug_line/.debug_info,把已有的"函数级/语句级 DWARF 行标记"导出成
|
# .debug_line/.debug_info,把已有的"函数级/语句级 DWARF 行标记"导出成
|
||||||
# <exe>.stackmap,供 exe 内运行时处理器在 Ctrl+C/Ctrl+Break 时把 CRT 到当前
|
# <exe>.stackmap,供 exe 内运行时处理器在 Ctrl+C/Ctrl+Break 时把 CRT 到当前
|
||||||
# 的返回地址逐帧翻译回 ".vp:行/函数"。以下函数只在开关开启时被调用;未开启时
|
# 的返回地址逐帧翻译回 ".vp:行/函数"。以下函数只在开关开启时被调用;未开启时
|
||||||
@@ -95,7 +95,7 @@ def _ReadPEImageLayout(exe_path: str) -> tuple[int, int, dict[str, tuple[int, in
|
|||||||
"""读取 PE 的 (ImageBase, SizeOfImage, 节表) ;非 PE / 解析失败返回 None。
|
"""读取 PE 的 (ImageBase, SizeOfImage, 节表) ;非 PE / 解析失败返回 None。
|
||||||
|
|
||||||
节表为 {节名: (节 RVA, 节 VirtualSize)}。写端需要这三样才能把 DWARF 地址严格
|
节表为 {节名: (节 RVA, 节 VirtualSize)}。写端需要这三样才能把 DWARF 地址严格
|
||||||
归一到"运行期 RVA"(运行期 dbgstack.vp 用 `帧地址 − GetModuleHandleA(NULL)`):
|
归一到"运行期 RVA"(运行期 traceback.vp 用 `帧地址 − GetModuleHandleA(NULL)`):
|
||||||
* 合法行 ⇔ 地址落在映像 VA 区间 [ImageBase, ImageBase + SizeOfImage);
|
* 合法行 ⇔ 地址落在映像 VA 区间 [ImageBase, ImageBase + SizeOfImage);
|
||||||
* "运行期可能出现的 RVA 区间"的粗判据 = [.text 节 RVA, SizeOfImage)。
|
* "运行期可能出现的 RVA 区间"的粗判据 = [.text 节 RVA, SizeOfImage)。
|
||||||
(原 _ReadPEImageBase 只返回 ImageBase,不足以做上述判定,故扩展为本函数。)
|
(原 _ReadPEImageBase 只返回 ImageBase,不足以做上述判定,故扩展为本函数。)
|
||||||
@@ -413,7 +413,7 @@ def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp
|
|||||||
hotspot_sites_file: str | None = None,
|
hotspot_sites_file: str | None = None,
|
||||||
hotspot_sites_all: bool = False,
|
hotspot_sites_all: bool = False,
|
||||||
hotspot_callees: list[str] | None = None,
|
hotspot_callees: list[str] | None = None,
|
||||||
ctrlc_stack_enabled: bool = False,
|
traceback_enabled: bool = False,
|
||||||
dwarf_enabled: bool = False) -> tuple[str, str, str]:
|
dwarf_enabled: bool = False) -> tuple[str, str, str]:
|
||||||
# 注意:不可跨任务复用 Phase2Translator/共享符号表!_translate_file 编译过程中
|
# 注意:不可跨任务复用 Phase2Translator/共享符号表!_translate_file 编译过程中
|
||||||
# 会向共享 SymbolTable 写入符号(import insert/类注册等),复用导致前文件的
|
# 会向共享 SymbolTable 写入符号(import insert/类注册等),复用导致前文件的
|
||||||
@@ -437,10 +437,10 @@ def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp
|
|||||||
trans.hotspot_sites_file = hotspot_sites_file
|
trans.hotspot_sites_file = hotspot_sites_file
|
||||||
trans.hotspot_sites_all = hotspot_sites_all
|
trans.hotspot_sites_all = hotspot_sites_all
|
||||||
trans.hotspot_callees = hotspot_callees
|
trans.hotspot_callees = hotspot_callees
|
||||||
# Ctrl+C 调用栈(--ctrlc-stack):worker 子进程无父进程属性,须显式透传
|
# Ctrl+C 调用栈(--traceback):worker 子进程无父进程属性,须显式透传
|
||||||
# (False=未启用,零影响);dbgstack 模块据此注入 Install() 启动期构造器
|
# (False=未启用,零影响);traceback 模块据此注入 Install() 启动期构造器
|
||||||
trans.ctrlc_stack_enabled = ctrlc_stack_enabled
|
trans.traceback_enabled = traceback_enabled
|
||||||
# 函数级 DWARF(--ctrlc-stack 时 include 模块也必须带行表,否则栈里落到
|
# 函数级 DWARF(--traceback 时 include 模块也必须带行表,否则栈里落到
|
||||||
# includes/* 的帧取不到源行):worker 子进程无父进程属性,须显式透传
|
# includes/* 的帧取不到源行):worker 子进程无父进程属性,须显式透传
|
||||||
# (False=未启用,零影响),仅用于 _translate_file 的 dwarf_enabled 形参
|
# (False=未启用,零影响),仅用于 _translate_file 的 dwarf_enabled 形参
|
||||||
trans.dwarf_enabled = dwarf_enabled
|
trans.dwarf_enabled = dwarf_enabled
|
||||||
@@ -736,7 +736,7 @@ def _BuildPyiCacheFile(temp_dir: str) -> str | None:
|
|||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
with open(p, 'r', encoding='utf-8') as f:
|
with open(p, 'r', encoding='utf-8') as f:
|
||||||
cache[p] = ast.parse(f.read())
|
cache[p] = ast.parse(f.read(), p)
|
||||||
except Exception:
|
except Exception:
|
||||||
cache[p] = None
|
cache[p] = None
|
||||||
try:
|
try:
|
||||||
@@ -776,7 +776,7 @@ def _PyiCacheWorkerInitializer(temp_dir: str) -> None:
|
|||||||
for p in _ScanPyiFiles(temp_dir):
|
for p in _ScanPyiFiles(temp_dir):
|
||||||
try:
|
try:
|
||||||
with open(p, 'r', encoding='utf-8') as f:
|
with open(p, 'r', encoding='utf-8') as f:
|
||||||
new_cache[p] = ast.parse(f.read())
|
new_cache[p] = ast.parse(f.read(), p)
|
||||||
except Exception:
|
except Exception:
|
||||||
new_cache[p] = None
|
new_cache[p] = None
|
||||||
if new_cache:
|
if new_cache:
|
||||||
@@ -914,7 +914,7 @@ class Phase2Translator:
|
|||||||
_include_sha1_cache: dict[str, str] = {}
|
_include_sha1_cache: dict[str, str] = {}
|
||||||
_include_exist_cache: dict[str, bool] = {}
|
_include_exist_cache: dict[str, bool] = {}
|
||||||
|
|
||||||
def __init__(self, src_root: str, temp_dir: str, output_dir: str, compile_cmd: str = 'llc', compile_flags: list[str] | None = None, linker_cmd: str | None = None, linker_flags: list[str] | None = None, linker_output: str | None = None, static_libs: list[str] | None = None, dynamic_libs: list[str] | None = None, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None, force_recompile_includes: bool = False, build_pyi_cache: bool = True, asan_mode: bool = False, dwarf_enabled: bool = False, ctrlc_stack_enabled: bool = False) -> None:
|
def __init__(self, src_root: str, temp_dir: str, output_dir: str, compile_cmd: str = 'llc', compile_flags: list[str] | None = None, linker_cmd: str | None = None, linker_flags: list[str] | None = None, linker_output: str | None = None, static_libs: list[str] | None = None, dynamic_libs: list[str] | None = None, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None, force_recompile_includes: bool = False, build_pyi_cache: bool = True, asan_mode: bool = False, dwarf_enabled: bool = False, traceback_enabled: bool = False) -> None:
|
||||||
self.src_root = os.path.abspath(src_root)
|
self.src_root = os.path.abspath(src_root)
|
||||||
self.temp_dir = os.path.abspath(temp_dir)
|
self.temp_dir = os.path.abspath(temp_dir)
|
||||||
self.output_dir = os.path.abspath(output_dir)
|
self.output_dir = os.path.abspath(output_dir)
|
||||||
@@ -950,12 +950,12 @@ class Phase2Translator:
|
|||||||
# !llvm.dbg.cu + 每函数 DISubprogram + 入口 DILocation,供 ASan /
|
# !llvm.dbg.cu + 每函数 DISubprogram + 入口 DILocation,供 ASan /
|
||||||
# llvm-symbolizer 反查 ".vp:函数起始行"。
|
# llvm-symbolizer 反查 ".vp:函数起始行"。
|
||||||
self.dwarf_enabled: bool = dwarf_enabled
|
self.dwarf_enabled: bool = dwarf_enabled
|
||||||
# Ctrl+C 调用栈(--ctrlc-stack):False=未启用(零影响,不调用任何外部
|
# Ctrl+C 调用栈(--traceback):False=未启用(零影响,不调用任何外部
|
||||||
# 工具、不写任何旁文件);True=链接成功后额外用 llvm-dwarfdump 把最终
|
# 工具、不写任何旁文件);True=链接成功后额外用 llvm-dwarfdump 把最终
|
||||||
# exe 的 DWARF 行表导出为 "<exe>.stackmap"(地址→源行旁文件,供 exe 内
|
# exe 的 DWARF 行表导出为 "<exe>.stackmap"(地址→源行旁文件,供 exe 内
|
||||||
# 运行时在 Ctrl+C/Ctrl+Break 时逐帧翻译返回地址)。要求 --dwarf 已开启
|
# 运行时在 Ctrl+C/Ctrl+Break 时逐帧翻译返回地址)。要求 --dwarf 已开启
|
||||||
# (Config 侧联动,否则行表为空),且目标为 Windows PE。
|
# (Config 侧联动,否则行表为空),且目标为 Windows PE。
|
||||||
self.ctrlc_stack_enabled: bool = ctrlc_stack_enabled
|
self.traceback_enabled: bool = traceback_enabled
|
||||||
# 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数
|
# 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数
|
||||||
# 落盘文件路径(每函数计时落同目录 hotspot_time.txt)。各模块运行时自带
|
# 落盘文件路径(每函数计时落同目录 hotspot_time.txt)。各模块运行时自带
|
||||||
# Ctrl+C 处理器、统一以追加方式写该路径;含 main 的模块在 main 入口清空一次
|
# Ctrl+C 处理器、统一以追加方式写该路径;含 main 的模块在 main 入口清空一次
|
||||||
@@ -1173,9 +1173,9 @@ class Phase2Translator:
|
|||||||
# 行为与改动前完全一致(零影响)。
|
# 行为与改动前完全一致(零影响)。
|
||||||
# --dwarf(函数级 DWARF)同理:缓存 .ll/.obj 不含 !llvm.dbg.cu,必须一并
|
# --dwarf(函数级 DWARF)同理:缓存 .ll/.obj 不含 !llvm.dbg.cu,必须一并
|
||||||
# 强制重译,否则开启开关后仍复用旧产物、看不到任何 .vp 行号。
|
# 强制重译,否则开启开关后仍复用旧产物、看不到任何 .vp 行号。
|
||||||
# --ctrlc-stack 依赖同一份 .debug_line 行表(Config 已联动开启 --dwarf),
|
# --traceback 依赖同一份 .debug_line 行表(Config 已联动开启 --dwarf),
|
||||||
# 这里显式纳入,避免将来联动改动后静默复用无 DI 的旧产物。
|
# 这里显式纳入,避免将来联动改动后静默复用无 DI 的旧产物。
|
||||||
hotspot_on: bool = bool(self.hotspot_sites_file) or bool(self.dwarf_enabled) or bool(self.ctrlc_stack_enabled)
|
hotspot_on: bool = bool(self.hotspot_sites_file) or bool(self.dwarf_enabled) or bool(self.traceback_enabled)
|
||||||
for i, src_path in enumerate(py_files, 1):
|
for i, src_path in enumerate(py_files, 1):
|
||||||
rel = os.path.relpath(src_path, self.src_root)
|
rel = os.path.relpath(src_path, self.src_root)
|
||||||
# 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已 parse 过)
|
# 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已 parse 过)
|
||||||
@@ -1786,7 +1786,7 @@ class Phase2Translator:
|
|||||||
# `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析:
|
# `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析:
|
||||||
# 否则此处 SyntaxError 会静默 return 原 code(自动注入 import _list/_dict
|
# 否则此处 SyntaxError 会静默 return 原 code(自动注入 import _list/_dict
|
||||||
# 失效),与主翻译路径(Shell/TransPyC 已展开)口径不一致。
|
# 失效),与主翻译路径(Shell/TransPyC 已展开)口径不一致。
|
||||||
tree = ast.parse(ExpandSoftKeywords(code))
|
tree = ast.parse(ExpandSoftKeywords(code), src_path)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
return code
|
return code
|
||||||
|
|
||||||
@@ -1851,10 +1851,10 @@ class Phase2Translator:
|
|||||||
"""翻译单个源文件,嵌入相关 .stub.ll 声明
|
"""翻译单个源文件,嵌入相关 .stub.ll 声明
|
||||||
|
|
||||||
dwarf_enabled: 函数级 DWARF 调试元数据(--dwarf),默认 False(零影响)。
|
dwarf_enabled: 函数级 DWARF 调试元数据(--dwarf),默认 False(零影响)。
|
||||||
App 源模块在 --dwarf/--asan/--ctrlc-stack 时传 True。include 文件默认
|
App 源模块在 --dwarf/--asan/--traceback 时传 True。include 文件默认
|
||||||
传 False(其 .obj 会落进跨项目共享的 includes.binary 预编译缓存,注入 DI
|
传 False(其 .obj 会落进跨项目共享的 includes.binary 预编译缓存,注入 DI
|
||||||
会污染该缓存);仅 --ctrlc-stack 时传 True——此时 include 的 .obj 改落
|
会污染该缓存);仅 --traceback 时传 True——此时 include 的 .obj 改落
|
||||||
隔离目录(includes.binary_ctrlcstack,见 _get_precompiled_obj_path),
|
隔离目录(includes.binary_traceback,见 _get_precompiled_obj_path),
|
||||||
不会污染共享缓存。
|
不会污染共享缓存。
|
||||||
"""
|
"""
|
||||||
_t0: float = time.time()
|
_t0: float = time.time()
|
||||||
@@ -1891,15 +1891,15 @@ class Phase2Translator:
|
|||||||
# 未启用时不设置 → 与改动前逐字节一致(零影响)
|
# 未启用时不设置 → 与改动前逐字节一致(零影响)
|
||||||
if dwarf_enabled:
|
if dwarf_enabled:
|
||||||
trans.translator.DwarfEnabled = True
|
trans.translator.DwarfEnabled = True
|
||||||
# Ctrl+C 调用栈(--ctrlc-stack):仅当本文件就是 dbgstack 模块时置位——
|
# Ctrl+C 调用栈(--traceback):仅当本文件就是 traceback 模块时置位——
|
||||||
# 只有该模块需要注入 @llvm.global_ctors 构造器调用自己的 Install()
|
# 只有该模块需要注入 @llvm.global_ctors 构造器调用自己的 Install()
|
||||||
# (见 BaseGen._RegisterDbgStackInstallViaCtor)。其它模块不设置该属性
|
# (见 BaseGen._RegisterTracebackInstallViaCtor)。其它模块不设置该属性
|
||||||
# → Gen.ctrlc_stack_ctor 保持 False,IR 与改动前逐字节一致(零影响)。
|
# → Gen.traceback_ctor 保持 False,IR 与改动前逐字节一致(零影响)。
|
||||||
_ctrlc_stack_ctor: bool = bool(
|
_traceback_ctor: bool = bool(
|
||||||
self.ctrlc_stack_enabled
|
self.traceback_enabled
|
||||||
and os.path.basename(src_path) in _CTRLC_STACK_MODULE_FILES)
|
and os.path.basename(src_path) in _TRACEBACK_MODULE_FILES)
|
||||||
if _ctrlc_stack_ctor:
|
if _traceback_ctor:
|
||||||
trans.translator.CtrlcStackCtor = True
|
trans.translator.TracebackCtor = True
|
||||||
trans.SliceLevel = self.slice_level
|
trans.SliceLevel = self.slice_level
|
||||||
trans.translator.SliceLevel = self.slice_level
|
trans.translator.SliceLevel = self.slice_level
|
||||||
trans.translator.SliceCount = 0
|
trans.translator.SliceCount = 0
|
||||||
@@ -1953,7 +1953,7 @@ class Phase2Translator:
|
|||||||
try:
|
try:
|
||||||
with open(sig_path, 'r', encoding='utf-8') as f:
|
with open(sig_path, 'r', encoding='utf-8') as f:
|
||||||
sig_content = f.read()
|
sig_content = f.read()
|
||||||
sig_tree = ast.parse(sig_content)
|
sig_tree = ast.parse(sig_content, sig_path)
|
||||||
for node in ast.iter_child_nodes(sig_tree):
|
for node in ast.iter_child_nodes(sig_tree):
|
||||||
if isinstance(node, ast.FunctionDef):
|
if isinstance(node, ast.FunctionDef):
|
||||||
if _check_annotation_for_export(node.returns):
|
if _check_annotation_for_export(node.returns):
|
||||||
@@ -2097,13 +2097,13 @@ class Phase2Translator:
|
|||||||
out_dir = os.path.dirname(out_path)
|
out_dir = os.path.dirname(out_path)
|
||||||
if out_dir:
|
if out_dir:
|
||||||
os.makedirs(out_dir, exist_ok=True)
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
# --ctrlc-stack:核对注入钩子确实生效——本模块要求注入 @llvm.global_ctors
|
# --traceback:核对注入钩子确实生效——本模块要求注入 @llvm.global_ctors
|
||||||
# 构造器(StructGen.finalize 里落成 .ll 文本之前),若产物中没有该全局
|
# 构造器(StructGen.finalize 里落成 .ll 文本之前),若产物中没有该全局
|
||||||
# 说明钩子不可用(如序列化路径被改动绕过),显式报错终止,不静默产出
|
# 说明钩子不可用(如序列化路径被改动绕过),显式报错终止,不静默产出
|
||||||
# 缺注册的产物(那会让整个功能静默失效)。
|
# 缺注册的产物(那会让整个功能静默失效)。
|
||||||
if _ctrlc_stack_ctor and 'llvm.global_ctors' not in result:
|
if _traceback_ctor and 'llvm.global_ctors' not in result:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'--ctrlc-stack: dbgstack 模块未注入 @llvm.global_ctors '
|
'--traceback: traceback 模块未注入 @llvm.global_ctors '
|
||||||
'(模块级收尾钩子未生效,不静默降级)')
|
'(模块级收尾钩子未生效,不静默降级)')
|
||||||
# 修复 LLVM 22+: external global 不能有初值,改为 linkonce
|
# 修复 LLVM 22+: external global 不能有初值,改为 linkonce
|
||||||
result = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', result, flags=re.MULTILINE)
|
result = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', result, flags=re.MULTILINE)
|
||||||
@@ -2218,8 +2218,8 @@ class Phase2Translator:
|
|||||||
return opt_r
|
return opt_r
|
||||||
compile_input = asan_ll
|
compile_input = asan_ll
|
||||||
cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, compile_input]
|
cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, compile_input]
|
||||||
if self.ctrlc_stack_enabled:
|
if self.traceback_enabled:
|
||||||
# --ctrlc-stack:让 llc 保留帧指针(-frame-pointer=all)。dbghelp 主路径
|
# --traceback:让 llc 保留帧指针(-frame-pointer=all)。dbghelp 主路径
|
||||||
# 不依赖帧指针,但符号表不可用时要靠 RBP 链兜底——llc 默认
|
# 不依赖帧指针,但符号表不可用时要靠 RBP 链兜底——llc 默认
|
||||||
# omit-frame-pointer 会让 RBP 链只走出 1~2 帧(实测"共 2 帧")。
|
# omit-frame-pointer 会让 RBP 链只走出 1~2 帧(实测"共 2 帧")。
|
||||||
# 只在开启该开关时加,关闭时命令与改前逐字一致。
|
# 只在开启该开关时加,关闭时命令与改前逐字一致。
|
||||||
@@ -2786,14 +2786,14 @@ class Phase2Translator:
|
|||||||
|
|
||||||
def _scan_include_libraries(self) -> None:
|
def _scan_include_libraries(self) -> None:
|
||||||
"""扫描 includes 目录,检测被使用的模块对应的库文件和源文件"""
|
"""扫描 includes 目录,检测被使用的模块对应的库文件和源文件"""
|
||||||
# --ctrlc-stack:强制纳入 dbgstack 模块 —— 无任何用户代码 import 它,
|
# --traceback:强制纳入 traceback 模块 —— 无任何用户代码 import 它,
|
||||||
# 不纳入就不会被翻译 ⇒ 产物没有 Install 符号可注入启动期构造器。
|
# 不纳入就不会被翻译 ⇒ 产物没有 Install 符号可注入启动期构造器。
|
||||||
# 直接借用既有的 used_includes 收集路径(下面按同一套 py_index /
|
# 直接借用既有的 used_includes 收集路径(下面按同一套 py_index /
|
||||||
# 传递依赖逻辑把它加入 extra_py_files,再交给 _compile_include_py_files
|
# 传递依赖逻辑把它加入 extra_py_files,再交给 _compile_include_py_files
|
||||||
# 编译并把 .obj 追加进 extra_link_files)。开关关闭时不触碰 used_includes
|
# 编译并把 .obj 追加进 extra_link_files)。开关关闭时不触碰 used_includes
|
||||||
# → 本函数行为与改动前逐字一致(零影响)。
|
# → 本函数行为与改动前逐字一致(零影响)。
|
||||||
if self.ctrlc_stack_enabled:
|
if self.traceback_enabled:
|
||||||
self.used_includes.add('dbgstack')
|
self.used_includes.add('traceback')
|
||||||
if not self.used_includes:
|
if not self.used_includes:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -2845,16 +2845,16 @@ class Phase2Translator:
|
|||||||
_vlog().info(f"\n[includes] 需要链接的库文件: {len(self.extra_link_files)} 个")
|
_vlog().info(f"\n[includes] 需要链接的库文件: {len(self.extra_link_files)} 个")
|
||||||
if self.extra_compile_files:
|
if self.extra_compile_files:
|
||||||
_vlog().info(f"[includes] 需要编译的源文件: {len(self.extra_compile_files)} 个")
|
_vlog().info(f"[includes] 需要编译的源文件: {len(self.extra_compile_files)} 个")
|
||||||
# --ctrlc-stack:断言 dbgstack 源文件确实被纳入待编译集合。找不到就必然
|
# --traceback:断言 traceback 源文件确实被纳入待编译集合。找不到就必然
|
||||||
# 得不到 Install 符号、构造器无处注入(功能静默失效)——显式报错终止,
|
# 得不到 Install 符号、构造器无处注入(功能静默失效)——显式报错终止,
|
||||||
# 不静默跳过(多半是 project.json 的 includes 未包含仓库的 includes 目录)。
|
# 不静默跳过(多半是 project.json 的 includes 未包含仓库的 includes 目录)。
|
||||||
if self.ctrlc_stack_enabled:
|
if self.traceback_enabled:
|
||||||
_dbgstack_found: bool = any(
|
_traceback_found: bool = any(
|
||||||
os.path.basename(_p) in _CTRLC_STACK_MODULE_FILES
|
os.path.basename(_p) in _TRACEBACK_MODULE_FILES
|
||||||
for _p in self.extra_py_files)
|
for _p in self.extra_py_files)
|
||||||
if not _dbgstack_found:
|
if not _traceback_found:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'--ctrlc-stack: 在 includes 目录中找不到 dbgstack.vp/.py,'
|
'--traceback: 在 includes 目录中找不到 traceback.vp/.py,'
|
||||||
'无法编译并链接 Install()(请确认 project.json 的 includes '
|
'无法编译并链接 Install()(请确认 project.json 的 includes '
|
||||||
f'包含仓库 includes 目录;已扫描: {self.include_dirs})')
|
f'包含仓库 includes 目录;已扫描: {self.include_dirs})')
|
||||||
|
|
||||||
@@ -3104,14 +3104,14 @@ class Phase2Translator:
|
|||||||
预编译目录位于 include 目录同级,命名为 {basename}.binary/。
|
预编译目录位于 include 目录同级,命名为 {basename}.binary/。
|
||||||
文件命名为 {sha1}.{triple_safe}.{rel_model}.obj,区分目标平台和重定位模型。
|
文件命名为 {sha1}.{triple_safe}.{rel_model}.obj,区分目标平台和重定位模型。
|
||||||
|
|
||||||
--ctrlc-stack:include 模块也带 DWARF 行表(栈帧要能落到 includes/* 的源行),
|
--traceback:include 模块也带 DWARF 行表(栈帧要能落到 includes/* 的源行),
|
||||||
此类 .obj 不得回写进跨项目共享的 includes.binary(会污染其它项目的预编译
|
此类 .obj 不得回写进跨项目共享的 includes.binary(会污染其它项目的预编译
|
||||||
缓存),改落**兄弟目录** {basename}.binary + 后缀(不新增层级,形态与
|
缓存),改落**兄弟目录** {basename}.binary + 后缀(不新增层级,形态与
|
||||||
ASan 变体目录 includes.binary_asan 一致)。
|
ASan 变体目录 includes.binary_asan 一致)。
|
||||||
关闭时后缀为空 → 路径与改动前逐字一致(含 --dwarf/--asan 单独开启的情形)。
|
关闭时后缀为空 → 路径与改动前逐字一致(含 --dwarf/--asan 单独开启的情形)。
|
||||||
--asan 与 --ctrlc-stack 同开:ASan 插桩也会改变 include obj 内容(见
|
--asan 与 --traceback 同开:ASan 插桩也会改变 include obj 内容(见
|
||||||
_llc_compile),两者产物不可互相复用,故再叠加 _asan 后缀区分
|
_llc_compile),两者产物不可互相复用,故再叠加 _asan 后缀区分
|
||||||
(优先级:是否隔离由 --ctrlc-stack 决定,--asan 只决定后缀再叠加 _asan)。
|
(优先级:是否隔离由 --traceback 决定,--asan 只决定后缀再叠加 _asan)。
|
||||||
"""
|
"""
|
||||||
AbsSrcPath: str = os.path.abspath(src_path)
|
AbsSrcPath: str = os.path.abspath(src_path)
|
||||||
RelModel: str = 'static'
|
RelModel: str = 'static'
|
||||||
@@ -3128,8 +3128,8 @@ class Phase2Translator:
|
|||||||
inc_parent: str = os.path.dirname(abs_inc)
|
inc_parent: str = os.path.dirname(abs_inc)
|
||||||
inc_base: str = os.path.basename(abs_inc)
|
inc_base: str = os.path.basename(abs_inc)
|
||||||
_iso_suffix: str = ''
|
_iso_suffix: str = ''
|
||||||
if self.ctrlc_stack_enabled:
|
if self.traceback_enabled:
|
||||||
_iso_suffix = '_ctrlcstack_asan' if self.asan_mode else '_ctrlcstack'
|
_iso_suffix = '_traceback_asan' if self.asan_mode else '_traceback'
|
||||||
precompiled_dir: str = os.path.join(inc_parent, inc_base + '.binary' + _iso_suffix)
|
precompiled_dir: str = os.path.join(inc_parent, inc_base + '.binary' + _iso_suffix)
|
||||||
triple_safe: str = (self.triple or 'default').replace(os.sep, '_').replace('/', '_').replace('\\', '_').replace(':', '_')
|
triple_safe: str = (self.triple or 'default').replace(os.sep, '_').replace('/', '_').replace('\\', '_').replace(':', '_')
|
||||||
return os.path.join(precompiled_dir, f"{sha1}.{triple_safe}.{RelModel}.obj")
|
return os.path.join(precompiled_dir, f"{sha1}.{triple_safe}.{RelModel}.obj")
|
||||||
@@ -3155,18 +3155,18 @@ class Phase2Translator:
|
|||||||
# 本函数行为与改动前逐字节一致(零影响)。
|
# 本函数行为与改动前逐字节一致(零影响)。
|
||||||
hotspot_on: bool = bool(self.hotspot_sites_file)
|
hotspot_on: bool = bool(self.hotspot_sites_file)
|
||||||
|
|
||||||
# --ctrlc-stack:include 模块也必须带 DWARF 行表(否则运行时 Ctrl+C 的栈里
|
# --traceback:include 模块也必须带 DWARF 行表(否则运行时 Ctrl+C 的栈里
|
||||||
# 落在 includes/*(memhub/string/viperlib/w32…)的帧取不到源行,只能落到
|
# 落在 includes/*(memhub/string/viperlib/w32…)的帧取不到源行,只能落到
|
||||||
# 邻行或 "+0x…(unknown)")。为此翻译时给 include 透传 dwarf_enabled=True
|
# 邻行或 "+0x…(unknown)")。为此翻译时给 include 透传 dwarf_enabled=True
|
||||||
# (见下方 worker/串行四处的 dwarf_enabled=self.ctrlc_stack_enabled),并把
|
# (见下方 worker/串行四处的 dwarf_enabled=self.traceback_enabled),并把
|
||||||
# 旧目标文件(output 目录 {sha1}.obj 及隔离缓存 includes.binary_ctrlcstack
|
# 旧目标文件(output 目录 {sha1}.obj 及隔离缓存 includes.binary_traceback
|
||||||
# 内的旧 .obj)一并失效重译——复用上一次无 DI 的产物会让行表静默缺失。
|
# 内的旧 .obj)一并失效重译——复用上一次无 DI 的产物会让行表静默缺失。
|
||||||
# 与 hotspot_on 处理方式一致;同时覆盖 dbgstack.vp(其 .obj 还须含
|
# 与 hotspot_on 处理方式一致;同时覆盖 traceback.vp(其 .obj 还须含
|
||||||
# @llvm.global_ctors 启动期构造器)。取舍:开启本开关时 include 目标文件每
|
# @llvm.global_ctors 启动期构造器)。取舍:开启本开关时 include 目标文件每
|
||||||
# 次构建都重译(放弃隔离缓存的复用,换取"必然带 DWARF"的正确性保证),
|
# 次构建都重译(放弃隔离缓存的复用,换取"必然带 DWARF"的正确性保证),
|
||||||
# 仅调试模式可接受。开关关闭时 include_dwarf=False,本函数行为与改动前
|
# 仅调试模式可接受。开关关闭时 include_dwarf=False,本函数行为与改动前
|
||||||
# 逐字节一致(零影响,含 --dwarf/--asan 单独开启的情形)。
|
# 逐字节一致(零影响,含 --dwarf/--asan 单独开启的情形)。
|
||||||
include_dwarf: bool = bool(self.ctrlc_stack_enabled)
|
include_dwarf: bool = bool(self.traceback_enabled)
|
||||||
|
|
||||||
# 构建 include 文件的模块 SHA1 映射(预计算所有文件,构建完整映射)
|
# 构建 include 文件的模块 SHA1 映射(预计算所有文件,构建完整映射)
|
||||||
include_ModuleSha1Map = self._build_current_ModuleSha1Map(None)
|
include_ModuleSha1Map = self._build_current_ModuleSha1Map(None)
|
||||||
@@ -3206,7 +3206,7 @@ class Phase2Translator:
|
|||||||
self._collect_inline_symbols(src_path)
|
self._collect_inline_symbols(src_path)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# --hotspot-sites / --ctrlc-stack:include 的 .py 未变时下面两处会因旧
|
# --hotspot-sites / --traceback:include 的 .py 未变时下面两处会因旧
|
||||||
# .obj 存在而直接跳过,复用上一次(未插桩 / 不含 DI)的目标文件。
|
# .obj 存在而直接跳过,复用上一次(未插桩 / 不含 DI)的目标文件。
|
||||||
# 启用时先让这两类旧 .obj 一并失效,使其必然走入 need_translate 重新
|
# 启用时先让这两类旧 .obj 一并失效,使其必然走入 need_translate 重新
|
||||||
# 翻译并嵌入热点计数/计时 IR 或 DWARF 行表。
|
# 翻译并嵌入热点计数/计时 IR 或 DWARF 行表。
|
||||||
@@ -3351,8 +3351,8 @@ class Phase2Translator:
|
|||||||
hotspot_sites_file=self.hotspot_sites_file,
|
hotspot_sites_file=self.hotspot_sites_file,
|
||||||
hotspot_sites_all=self.hotspot_sites_all,
|
hotspot_sites_all=self.hotspot_sites_all,
|
||||||
hotspot_callees=self.hotspot_callees,
|
hotspot_callees=self.hotspot_callees,
|
||||||
ctrlc_stack_enabled=self.ctrlc_stack_enabled,
|
traceback_enabled=self.traceback_enabled,
|
||||||
dwarf_enabled=self.ctrlc_stack_enabled
|
dwarf_enabled=self.traceback_enabled
|
||||||
)
|
)
|
||||||
in_progress[future] = item
|
in_progress[future] = item
|
||||||
|
|
||||||
@@ -3372,8 +3372,8 @@ class Phase2Translator:
|
|||||||
hotspot_sites_file=self.hotspot_sites_file,
|
hotspot_sites_file=self.hotspot_sites_file,
|
||||||
hotspot_sites_all=self.hotspot_sites_all,
|
hotspot_sites_all=self.hotspot_sites_all,
|
||||||
hotspot_callees=self.hotspot_callees,
|
hotspot_callees=self.hotspot_callees,
|
||||||
ctrlc_stack_enabled=self.ctrlc_stack_enabled,
|
traceback_enabled=self.traceback_enabled,
|
||||||
dwarf_enabled=self.ctrlc_stack_enabled
|
dwarf_enabled=self.traceback_enabled
|
||||||
)
|
)
|
||||||
in_progress[future] = item
|
in_progress[future] = item
|
||||||
remaining.clear()
|
remaining.clear()
|
||||||
@@ -3417,12 +3417,12 @@ class Phase2Translator:
|
|||||||
for item in need_translate:
|
for item in need_translate:
|
||||||
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
|
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
|
||||||
_vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll")
|
_vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll")
|
||||||
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.ctrlc_stack_enabled)
|
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled)
|
||||||
else:
|
else:
|
||||||
item = need_translate[0]
|
item = need_translate[0]
|
||||||
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
|
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
|
||||||
_vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll")
|
_vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll")
|
||||||
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.ctrlc_stack_enabled)
|
self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled)
|
||||||
|
|
||||||
# ========== 阶段2:串行后处理(dso_local + stub_decls 注入)==========
|
# ========== 阶段2:串行后处理(dso_local + stub_decls 注入)==========
|
||||||
compile_tasks = [] # [(dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)]
|
compile_tasks = [] # [(dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)]
|
||||||
@@ -3435,16 +3435,16 @@ class Phase2Translator:
|
|||||||
with open(ll_path, 'r', encoding='utf-8') as f:
|
with open(ll_path, 'r', encoding='utf-8') as f:
|
||||||
ll_content = f.read()
|
ll_content = f.read()
|
||||||
|
|
||||||
# --ctrlc-stack 提醒:include 模块若发射了 define 却不带 DI(!llvm.dbg.cu),
|
# --traceback 提醒:include 模块若发射了 define 却不带 DI(!llvm.dbg.cu),
|
||||||
# 其 .debug_line 行为空 ⇒ 运行时 Ctrl+C 栈里该 include 帧取不到源行
|
# 其 .debug_line 行为空 ⇒ 运行时 Ctrl+C 栈里该 include 帧取不到源行
|
||||||
# (会退化为 +0x… (unknown))。
|
# (会退化为 +0x… (unknown))。
|
||||||
# **只警告、不报错**:像 includes/stdarg.vp 这类模块的 define 是"合成"的
|
# **只警告、不报错**:像 includes/stdarg.vp 这类模块的 define 是"合成"的
|
||||||
# (由 extern/sugar 派生、无 .vp 函数体),本就不会有源级 DI —— 按 fail-fast
|
# (由 extern/sugar 派生、无 .vp 函数体),本就不会有源级 DI —— 按 fail-fast
|
||||||
# 报错会把这类模块全部误杀;而"行号缺失"只降低可读性,不是编译正确性问题。
|
# 报错会把这类模块全部误杀;而"行号缺失"只降低可读性,不是编译正确性问题。
|
||||||
if self.ctrlc_stack_enabled and re.search(r'^define ', ll_content, re.MULTILINE) \
|
if self.traceback_enabled and re.search(r'^define ', ll_content, re.MULTILINE) \
|
||||||
and '!llvm.dbg.cu' not in ll_content:
|
and '!llvm.dbg.cu' not in ll_content:
|
||||||
_vlog().warning(
|
_vlog().warning(
|
||||||
f"--ctrlc-stack: include 模块 {module_name}.py 发射了 define 但 IR 未携带 "
|
f"--traceback: include 模块 {module_name}.py 发射了 define 但 IR 未携带 "
|
||||||
f"DWARF (!llvm.dbg.cu)——该模块内联帧在 Ctrl+C 栈里将只显示地址: {ll_path}")
|
f"DWARF (!llvm.dbg.cu)——该模块内联帧在 Ctrl+C 栈里将只显示地址: {ll_path}")
|
||||||
|
|
||||||
# dso_local 正则标注(使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare)
|
# dso_local 正则标注(使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare)
|
||||||
@@ -3602,8 +3602,8 @@ class Phase2Translator:
|
|||||||
result = compile_results.get(task)
|
result = compile_results.get(task)
|
||||||
if result and result.returncode == 0:
|
if result and result.returncode == 0:
|
||||||
self.extra_link_files.append(obj_path)
|
self.extra_link_files.append(obj_path)
|
||||||
# 复制到 includes.binary 预编译目录(--ctrlc-stack 时 precompiled_obj
|
# 复制到 includes.binary 预编译目录(--traceback 时 precompiled_obj
|
||||||
# 指向隔离目录 includes.binary_ctrlcstack,不会污染共享缓存)
|
# 指向隔离目录 includes.binary_traceback,不会污染共享缓存)
|
||||||
if precompiled_obj:
|
if precompiled_obj:
|
||||||
precompiled_dir = os.path.dirname(precompiled_obj)
|
precompiled_dir = os.path.dirname(precompiled_obj)
|
||||||
try:
|
try:
|
||||||
@@ -3611,12 +3611,12 @@ class Phase2Translator:
|
|||||||
os.makedirs(precompiled_dir, exist_ok=True)
|
os.makedirs(precompiled_dir, exist_ok=True)
|
||||||
shutil.copy2(obj_path, precompiled_obj)
|
shutil.copy2(obj_path, precompiled_obj)
|
||||||
except Exception as _e:
|
except Exception as _e:
|
||||||
# --ctrlc-stack fail-fast:隔离目录创建 / 写回失败必须显式终止,
|
# --traceback fail-fast:隔离目录创建 / 写回失败必须显式终止,
|
||||||
# 不静默降级(否则本次构建的 include obj 拿不到、或落到共享缓存
|
# 不静默降级(否则本次构建的 include obj 拿不到、或落到共享缓存
|
||||||
# 之外的位置,DWARF 行表与后续复用的前提都被破坏)。
|
# 之外的位置,DWARF 行表与后续复用的前提都被破坏)。
|
||||||
if self.ctrlc_stack_enabled:
|
if self.traceback_enabled:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"--ctrlc-stack: include 预编译缓存写入隔离目录失败: "
|
f"--traceback: include 预编译缓存写入隔离目录失败: "
|
||||||
f"{precompiled_obj}: {_e}") from _e
|
f"{precompiled_obj}: {_e}") from _e
|
||||||
if _config_mode == "strict":
|
if _config_mode == "strict":
|
||||||
_vlog().warning(f"预编译缓存写入失败: {_e}")
|
_vlog().warning(f"预编译缓存写入失败: {_e}")
|
||||||
@@ -4186,7 +4186,7 @@ class Phase2Translator:
|
|||||||
break
|
break
|
||||||
|
|
||||||
def _WriteStackMap(self, exe_path: str) -> None:
|
def _WriteStackMap(self, exe_path: str) -> None:
|
||||||
"""--ctrlc-stack:链接成功后导出"地址→源行"旁文件 <exe>.stackmap。
|
"""--traceback:链接成功后导出"地址→源行"旁文件 <exe>.stackmap。
|
||||||
|
|
||||||
用 llvm-dwarfdump 解析最终 PE 的 .debug_line(地址→文件/行)与
|
用 llvm-dwarfdump 解析最终 PE 的 .debug_line(地址→文件/行)与
|
||||||
.debug_info(DISubprogram 的 low/high_pc + DW_AT_name → 函数名),
|
.debug_info(DISubprogram 的 low/high_pc + DW_AT_name → 函数名),
|
||||||
@@ -4198,7 +4198,7 @@ class Phase2Translator:
|
|||||||
rva(u64) + file_off(u32) + line(u32) + func_off(u32)
|
rva(u64) + file_off(u32) + line(u32) + func_off(u32)
|
||||||
字符串池 str_bytes 字节(紧接记录区,UTF-8,全部以 '\\0' 结尾)
|
字符串池 str_bytes 字节(紧接记录区,UTF-8,全部以 '\\0' 结尾)
|
||||||
|
|
||||||
口径(必须与运行期严格一致):运行期 dbgstack.vp 取
|
口径(必须与运行期严格一致):运行期 traceback.vp 取
|
||||||
`rva = 帧地址 − GetModuleHandleA(NULL)`,即"链接期 ImageBase 之上的偏移",
|
`rva = 帧地址 − GetModuleHandleA(NULL)`,即"链接期 ImageBase 之上的偏移",
|
||||||
故写端唯一正确口径是 **`RVA = dwarfdump 打印的地址 − PE.ImageBase`**。
|
故写端唯一正确口径是 **`RVA = dwarfdump 打印的地址 − PE.ImageBase`**。
|
||||||
|
|
||||||
@@ -4214,27 +4214,27 @@ class Phase2Translator:
|
|||||||
的记录保留(RVA = 地址 − ImageBase);其余(未重定位的 0、越界值)一律**丢弃并
|
的记录保留(RVA = 地址 − ImageBase);其余(未重定位的 0、越界值)一律**丢弃并
|
||||||
统计**(不静默留垃圾条目)。同一 RVA 只保留行号最大的一条。
|
统计**(不静默留垃圾条目)。同一 RVA 只保留行号最大的一条。
|
||||||
|
|
||||||
写出前自检(任一不满足即 fail-fast 抛异常,遵循 --ctrlc-stack 既有风格):
|
写出前自检(任一不满足即 fail-fast 抛异常,遵循 --traceback 既有风格):
|
||||||
记录数 / min~max RVA 与 SizeOfImage 的关系 / 所有 RVA ∈ [0, SizeOfImage) /
|
记录数 / min~max RVA 与 SizeOfImage 的关系 / 所有 RVA ∈ [0, SizeOfImage) /
|
||||||
RVA 严格升序 / 至少若干条落在运行期可能出现的 RVA 区间
|
RVA 严格升序 / 至少若干条落在运行期可能出现的 RVA 区间
|
||||||
(粗判据 [.text 节 RVA, SizeOfImage))。
|
(粗判据 [.text 节 RVA, SizeOfImage))。
|
||||||
|
|
||||||
任何一步失败都直接抛异常终止编译(--ctrlc-stack 的硬要求,无兜底)。
|
任何一步失败都直接抛异常终止编译(--traceback 的硬要求,无兜底)。
|
||||||
"""
|
"""
|
||||||
import struct
|
import struct
|
||||||
import bisect
|
import bisect
|
||||||
|
|
||||||
def _Fail(msg: str) -> None:
|
def _Fail(msg: str) -> None:
|
||||||
full: str = msg + '(这是 --ctrlc-stack 的要求)'
|
full: str = msg + '(这是 --traceback 的要求)'
|
||||||
_vlog().error(full)
|
_vlog().error(full)
|
||||||
raise RuntimeError(full)
|
raise RuntimeError(full)
|
||||||
|
|
||||||
layout = _ReadPEImageLayout(exe_path)
|
layout = _ReadPEImageLayout(exe_path)
|
||||||
if layout is None:
|
if layout is None:
|
||||||
_Fail(f"--ctrlc-stack 只支持 Windows PE 产物,但 {exe_path} 不是有效 PE")
|
_Fail(f"--traceback 只支持 Windows PE 产物,但 {exe_path} 不是有效 PE")
|
||||||
image_base, size_of_image, sections = layout
|
image_base, size_of_image, sections = layout
|
||||||
if size_of_image <= 0:
|
if size_of_image <= 0:
|
||||||
_Fail(f"--ctrlc-stack 读到的 PE.SizeOfImage 非正({size_of_image}),无法界定 RVA 域")
|
_Fail(f"--traceback 读到的 PE.SizeOfImage 非正({size_of_image}),无法界定 RVA 域")
|
||||||
img_lo: int = image_base
|
img_lo: int = image_base
|
||||||
img_hi: int = image_base + size_of_image
|
img_hi: int = image_base + size_of_image
|
||||||
# 运行期可能出现的 RVA 区间的**粗判据**下界:.text 节 RVA(缺失时退化为最小节 RVA)
|
# 运行期可能出现的 RVA 区间的**粗判据**下界:.text 节 RVA(缺失时退化为最小节 RVA)
|
||||||
@@ -4245,22 +4245,22 @@ class Phase2Translator:
|
|||||||
|
|
||||||
dwarfdump: str | None = shutil.which('llvm-dwarfdump') or shutil.which('llvm-dwarfdump.exe')
|
dwarfdump: str | None = shutil.which('llvm-dwarfdump') or shutil.which('llvm-dwarfdump.exe')
|
||||||
if not dwarfdump:
|
if not dwarfdump:
|
||||||
_Fail("--ctrlc-stack 找不到 llvm-dwarfdump(请确认 PATH 内含 LLVM 工具)")
|
_Fail("--traceback 找不到 llvm-dwarfdump(请确认 PATH 内含 LLVM 工具)")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
proc = subprocess.run(
|
proc = subprocess.run(
|
||||||
[dwarfdump, '--debug-line', '--debug-info', exe_path],
|
[dwarfdump, '--debug-line', '--debug-info', exe_path],
|
||||||
capture_output=True)
|
capture_output=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_Fail(f"--ctrlc-stack 调用 llvm-dwarfdump 失败: {e}")
|
_Fail(f"--traceback 调用 llvm-dwarfdump 失败: {e}")
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
_Fail(f"--ctrlc-stack 的 llvm-dwarfdump 返回 {proc.returncode}: "
|
_Fail(f"--traceback 的 llvm-dwarfdump 返回 {proc.returncode}: "
|
||||||
+ proc.stderr.decode('utf-8', errors='replace').strip())
|
+ proc.stderr.decode('utf-8', errors='replace').strip())
|
||||||
dump_text: str = proc.stdout.decode('utf-8', errors='replace')
|
dump_text: str = proc.stdout.decode('utf-8', errors='replace')
|
||||||
|
|
||||||
rows: list[tuple[int, str, int]] = _ParseDwarfLineRows(dump_text)
|
rows: list[tuple[int, str, int]] = _ParseDwarfLineRows(dump_text)
|
||||||
if not rows:
|
if not rows:
|
||||||
_Fail(f"--ctrlc-stack 未能从 {exe_path} 解析出任何 DWARF 行记录(.debug_line 为空?)")
|
_Fail(f"--traceback 未能从 {exe_path} 解析出任何 DWARF 行记录(.debug_line 为空?)")
|
||||||
|
|
||||||
# ---- 行记录:逐条归一化为"运行期 RVA";不能归一的一律丢弃并统计 ----
|
# ---- 行记录:逐条归一化为"运行期 RVA";不能归一的一律丢弃并统计 ----
|
||||||
dropped_rows: int = 0
|
dropped_rows: int = 0
|
||||||
@@ -4278,12 +4278,12 @@ class Phase2Translator:
|
|||||||
best[rva] = (path, line)
|
best[rva] = (path, line)
|
||||||
if dropped_rows:
|
if dropped_rows:
|
||||||
_vlog().warning(
|
_vlog().warning(
|
||||||
f" [--ctrlc-stack] 丢弃 {dropped_rows} 条无法归一化的 DWARF 行记录"
|
f" [--traceback] 丢弃 {dropped_rows} 条无法归一化的 DWARF 行记录"
|
||||||
f"(地址不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;判定原因:多为被连接器"
|
f"(地址不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;判定原因:多为被连接器"
|
||||||
f" COMDAT 去重丢弃的泛型副本,其地址恰为 0)示例地址: "
|
f" COMDAT 去重丢弃的泛型副本,其地址恰为 0)示例地址: "
|
||||||
+ ", ".join(f"0x{a:x}" for a in dropped_row_samples))
|
+ ", ".join(f"0x{a:x}" for a in dropped_row_samples))
|
||||||
if not best:
|
if not best:
|
||||||
_Fail(f"--ctrlc-stack 从 {exe_path} 得到的行记录全部无法归一到运行期 RVA 域")
|
_Fail(f"--traceback 从 {exe_path} 得到的行记录全部无法归一到运行期 RVA 域")
|
||||||
|
|
||||||
# ---- 子程序(函数名):同样逐条归一化;不能归一的丢弃 ----
|
# ---- 子程序(函数名):同样逐条归一化;不能归一的丢弃 ----
|
||||||
funcs: list[tuple[int, int, str]] = _ParseDwarfSubprograms(dump_text)
|
funcs: list[tuple[int, int, str]] = _ParseDwarfSubprograms(dump_text)
|
||||||
@@ -4302,7 +4302,7 @@ class Phase2Translator:
|
|||||||
func_ranges.append((lo_rva, hi_rva, name))
|
func_ranges.append((lo_rva, hi_rva, name))
|
||||||
if dropped_funcs:
|
if dropped_funcs:
|
||||||
_vlog().warning(
|
_vlog().warning(
|
||||||
f" [--ctrlc-stack] 丢弃 {dropped_funcs} 个无法归一化的 DWARF 子程序"
|
f" [--traceback] 丢弃 {dropped_funcs} 个无法归一化的 DWARF 子程序"
|
||||||
f"(low_pc 不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;同上为去重副本)")
|
f"(low_pc 不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;同上为去重副本)")
|
||||||
func_ranges.sort(key=lambda f: f[0])
|
func_ranges.sort(key=lambda f: f[0])
|
||||||
func_lows: list[int] = [f[0] for f in func_ranges]
|
func_lows: list[int] = [f[0] for f in func_ranges]
|
||||||
@@ -4341,19 +4341,19 @@ class Phase2Translator:
|
|||||||
max_rva: int = rvas[-1]
|
max_rva: int = rvas[-1]
|
||||||
for i in range(1, len(rvas)):
|
for i in range(1, len(rvas)):
|
||||||
if rvas[i] <= rvas[i - 1]:
|
if rvas[i] <= rvas[i - 1]:
|
||||||
_Fail(f"--ctrlc-stack 自检失败:记录 RVA 非严格升序"
|
_Fail(f"--traceback 自检失败:记录 RVA 非严格升序"
|
||||||
f"(第 {i} 条 0x{rvas[i]:x} <= 前一条 0x{rvas[i - 1]:x},会破坏运行期二分查找)")
|
f"(第 {i} 条 0x{rvas[i]:x} <= 前一条 0x{rvas[i - 1]:x},会破坏运行期二分查找)")
|
||||||
outside: int = sum(1 for r in rvas if not (0 <= r < size_of_image))
|
outside: int = sum(1 for r in rvas if not (0 <= r < size_of_image))
|
||||||
if outside:
|
if outside:
|
||||||
_Fail(f"--ctrlc-stack 自检失败:有 {outside} 条记录的 RVA 落在 [0, SizeOfImage=0x{size_of_image:x}) 之外")
|
_Fail(f"--traceback 自检失败:有 {outside} 条记录的 RVA 落在 [0, SizeOfImage=0x{size_of_image:x}) 之外")
|
||||||
# 运行期可能出现的 RVA 区间(粗判据):[.text 节 RVA, SizeOfImage)。一条都没有 ⇒ 口径又错了。
|
# 运行期可能出现的 RVA 区间(粗判据):[.text 节 RVA, SizeOfImage)。一条都没有 ⇒ 口径又错了。
|
||||||
plausible: int = sum(1 for r in rvas if text_rva <= r < size_of_image)
|
plausible: int = sum(1 for r in rvas if text_rva <= r < size_of_image)
|
||||||
if plausible <= 0:
|
if plausible <= 0:
|
||||||
_Fail(f"--ctrlc-stack 自检失败:没有任何记录的 RVA 落在运行期可能出现的区间"
|
_Fail(f"--traceback 自检失败:没有任何记录的 RVA 落在运行期可能出现的区间"
|
||||||
f" [0x{text_rva:x}(.text RVA), 0x{size_of_image:x}(SizeOfImage)) 内"
|
f" [0x{text_rva:x}(.text RVA), 0x{size_of_image:x}(SizeOfImage)) 内"
|
||||||
f"(RVA 口径疑似又错了)")
|
f"(RVA 口径疑似又错了)")
|
||||||
_vlog().info(
|
_vlog().info(
|
||||||
f" [--ctrlc-stack] 自检: 记录={len(records)} RVA范围=[0x{min_rva:x}, 0x{max_rva:x}]"
|
f" [--traceback] 自检: 记录={len(records)} RVA范围=[0x{min_rva:x}, 0x{max_rva:x}]"
|
||||||
f" PE.SizeOfImage=0x{size_of_image:x}({size_of_image})"
|
f" PE.SizeOfImage=0x{size_of_image:x}({size_of_image})"
|
||||||
f" 各节最高结束 RVA=0x{max_sec_end:x}"
|
f" 各节最高结束 RVA=0x{max_sec_end:x}"
|
||||||
f" | 全部∈[0,SizeOfImage)=是 严格升序=是"
|
f" | 全部∈[0,SizeOfImage)=是 严格升序=是"
|
||||||
@@ -4371,9 +4371,9 @@ class Phase2Translator:
|
|||||||
with open(map_path, 'wb') as f:
|
with open(map_path, 'wb') as f:
|
||||||
f.write(out)
|
f.write(out)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_Fail(f"--ctrlc-stack 写出旁文件失败 {map_path}: {e}")
|
_Fail(f"--traceback 写出旁文件失败 {map_path}: {e}")
|
||||||
_vlog().success(
|
_vlog().success(
|
||||||
f" [--ctrlc-stack] 已写出地址→源行旁文件: {map_path}"
|
f" [--traceback] 已写出地址→源行旁文件: {map_path}"
|
||||||
f"({len(records)} 条记录,{len(out)} 字节)")
|
f"({len(records)} 条记录,{len(out)} 字节)")
|
||||||
|
|
||||||
def _link_obj_files(self, active_sha1s: set[str]) -> None:
|
def _link_obj_files(self, active_sha1s: set[str]) -> None:
|
||||||
@@ -4504,13 +4504,13 @@ SECTIONS {
|
|||||||
lib_flags.append('-L' + self.asan_rt_dir)
|
lib_flags.append('-L' + self.asan_rt_dir)
|
||||||
lib_flags.append('-l:libclang_rt.asan_dynamic-x86_64.dll.a')
|
lib_flags.append('-l:libclang_rt.asan_dynamic-x86_64.dll.a')
|
||||||
lib_flags.append('-l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a')
|
lib_flags.append('-l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a')
|
||||||
# Ctrl+C 栈转储(--ctrlc-stack):includes/dbgstack.vp 的主回溯路径用
|
# Ctrl+C 栈转储(--traceback):includes/traceback.vp 的主回溯路径用
|
||||||
# StackWalk64 / SymInitialize / SymFunctionTableAccess64 / SymGetModuleBase64
|
# StackWalk64 / SymInitialize / SymFunctionTableAccess64 / SymGetModuleBase64
|
||||||
# (均在 dbghelp),故开启该开关时必须显式链接 -ldbghelp,否则链接期报未定义符号。
|
# (均在 dbghelp),故开启该开关时必须显式链接 -ldbghelp,否则链接期报未定义符号。
|
||||||
# 与 ASan 同理:库要排在目标文件之后 ⇒ 追加到 lib_flags。
|
# 与 ASan 同理:库要排在目标文件之后 ⇒ 追加到 lib_flags。
|
||||||
# (若确实无法链 dbghelp:把 includes/dbgstack.vp 的 DBGSTACK_USE_DBGHELP 置 0,
|
# (若确实无法链 dbghelp:把 includes/traceback.vp 的 TRACEBACK_USE_DBGHELP 置 0,
|
||||||
# 退化为纯 RBP 链回溯,此时不该再带本 flag。)
|
# 退化为纯 RBP 链回溯,此时不该再带本 flag。)
|
||||||
if self.ctrlc_stack_enabled:
|
if self.traceback_enabled:
|
||||||
lib_flags.append('-ldbghelp')
|
lib_flags.append('-ldbghelp')
|
||||||
cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags
|
cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags
|
||||||
_vlog().info(f" 执行: {' '.join(cmd)}")
|
_vlog().info(f" 执行: {' '.join(cmd)}")
|
||||||
@@ -4564,8 +4564,8 @@ SECTIONS {
|
|||||||
_vlog().error(f"\n链接异常,立即终止编译。")
|
_vlog().error(f"\n链接异常,立即终止编译。")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# --ctrlc-stack:链接成功后导出"地址→源行"旁文件(<exe>.stackmap)。
|
# --traceback:链接成功后导出"地址→源行"旁文件(<exe>.stackmap)。
|
||||||
# 刻意放在上面的链接 try/except 之外,使导出阶段的 fail-fast RuntimeError
|
# 刻意放在上面的链接 try/except 之外,使导出阶段的 fail-fast RuntimeError
|
||||||
# 直接向上抛出终止编译,而不被"链接异常兜底"吞掉。
|
# 直接向上抛出终止编译,而不被"链接异常兜底"吞掉。
|
||||||
if self.ctrlc_stack_enabled:
|
if self.traceback_enabled:
|
||||||
self._WriteStackMap(exe_path)
|
self._WriteStackMap(exe_path)
|
||||||
@@ -78,10 +78,11 @@ def parse_python_file(filepath: str) -> tuple[str, ast.Module] | tuple[str, None
|
|||||||
except Exception:
|
except Exception:
|
||||||
return ('', None)
|
return ('', None)
|
||||||
try:
|
try:
|
||||||
# 软关键字展开:fn/fun/func/function -> def(行号不变),供 ast.parse 使用。
|
# 软关键字/语法糖展开:fn/fun/func/function -> def、enum 块 -> class X(t.CEnum)
|
||||||
|
# (均同行替换,行号不变),供 ast.parse 使用。
|
||||||
# 注意 content 保持原始文本返回(compute_sha1 必须与 Phase1 一致),
|
# 注意 content 保持原始文本返回(compute_sha1 必须与 Phase1 一致),
|
||||||
# 仅 tree 基于展开后的代码解析。
|
# 仅 tree 基于展开后的代码解析。
|
||||||
tree: ast.Module = ast.parse(ExpandSoftKeywords(content))
|
tree: ast.Module = ast.parse(ExpandSoftKeywords(content), filepath)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
return (content, None)
|
return (content, None)
|
||||||
_FILE_AST_CACHE[norm_path] = (mtime, content, tree)
|
_FILE_AST_CACHE[norm_path] = (mtime, content, tree)
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class TransPyC:
|
|||||||
f.write('')
|
f.write('')
|
||||||
|
|
||||||
# 解析AST并提取符号
|
# 解析AST并提取符号
|
||||||
tree: ast.Module = ast.parse(content)
|
tree: ast.Module = ast.parse(content, SymbolFile.FilePath or '<TransPyC:PreProcessSymbol>')
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
with open(DebugFile, 'a', encoding=SymbolFile.encoding) as f:
|
with open(DebugFile, 'a', encoding=SymbolFile.encoding) as f:
|
||||||
@@ -450,7 +450,7 @@ class TransPyC:
|
|||||||
self.translator.Content = Content
|
self.translator.Content = Content
|
||||||
|
|
||||||
# 解析Python代码为AST
|
# 解析Python代码为AST
|
||||||
Tree: ast.Module = ast.parse(Content)
|
Tree: ast.Module = ast.parse(Content, InputFile)
|
||||||
|
|
||||||
# 解析文件以提取类型信息(用于 EmbeddedAssignments)
|
# 解析文件以提取类型信息(用于 EmbeddedAssignments)
|
||||||
self.translator.ParsePythonFile(InputFile, encoding)
|
self.translator.ParsePythonFile(InputFile, encoding)
|
||||||
@@ -538,13 +538,13 @@ class TransPyC:
|
|||||||
self.translator.OriginalLines = self.code.split('\n')
|
self.translator.OriginalLines = self.code.split('\n')
|
||||||
|
|
||||||
# 解析Python代码为AST
|
# 解析Python代码为AST
|
||||||
Tree: ast.Module = ast.parse(self.code)
|
Tree: ast.Module = ast.parse(self.code, SourceFilename or '<TransPyC:Convert>')
|
||||||
|
|
||||||
# 从源代码直接预扫描 import 语句,注册别名
|
# 从源代码直接预扫描 import 语句,注册别名
|
||||||
# 注意:不能依赖 Tree 中的 import 节点,因为 ParsePythonFile 可能修改了 AST
|
# 注意:不能依赖 Tree 中的 import 节点,因为 ParsePythonFile 可能修改了 AST
|
||||||
if not getattr(self.translator, '_ImportedModules', None):
|
if not getattr(self.translator, '_ImportedModules', None):
|
||||||
self.translator._ImportedModules = set()
|
self.translator._ImportedModules = set()
|
||||||
_prescan_tree: ast.Module = ast.parse(self.code)
|
_prescan_tree: ast.Module = ast.parse(self.code, SourceFilename or '<TransPyC:Convert>')
|
||||||
for _node in ast.iter_child_nodes(_prescan_tree):
|
for _node in ast.iter_child_nodes(_prescan_tree):
|
||||||
if isinstance(_node, ast.Import):
|
if isinstance(_node, ast.Import):
|
||||||
for _alias in _node.names:
|
for _alias in _node.names:
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ class PythonToStubConverter:
|
|||||||
HasImportT: bool = False
|
HasImportT: bool = False
|
||||||
HasImportC: bool = False
|
HasImportC: bool = False
|
||||||
try:
|
try:
|
||||||
tree: ast.Module = ast.parse(PyContent)
|
# 传来源名:解析失败时 vast 的报错/回退诊断才带得上模块名(否则记为 <unknown>)
|
||||||
|
tree: ast.Module = ast.parse(PyContent, f'{ModuleName}.vp')
|
||||||
for node in tree.body:
|
for node in tree.body:
|
||||||
if isinstance(node, ast.Import):
|
if isinstance(node, ast.Import):
|
||||||
for alias in node.names:
|
for alias in node.names:
|
||||||
@@ -127,7 +128,7 @@ class PythonToStubConverter:
|
|||||||
try:
|
try:
|
||||||
if tree is None:
|
if tree is None:
|
||||||
# 第一遍解析失败(非语法类异常):重试一次,保持原"主循环重新解析"语义
|
# 第一遍解析失败(非语法类异常):重试一次,保持原"主循环重新解析"语义
|
||||||
tree = ast.parse(PyContent)
|
tree = ast.parse(PyContent, f'{ModuleName}.vp')
|
||||||
|
|
||||||
# 第一遍扫描:收集 Postdefinition 变量
|
# 第一遍扫描:收集 Postdefinition 变量
|
||||||
PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]}
|
PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]}
|
||||||
@@ -492,12 +493,23 @@ class PythonToStubConverter:
|
|||||||
if SourceLines:
|
if SourceLines:
|
||||||
StartLine: int = node.lineno - 1
|
StartLine: int = node.lineno - 1
|
||||||
EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1
|
EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1
|
||||||
for i in range(StartLine, min(EndLine, len(SourceLines))):
|
Copied: list[str] = [SourceLines[i]
|
||||||
lines.append(SourceLines[i])
|
for i in range(StartLine, min(EndLine, len(SourceLines)))]
|
||||||
|
# 丢弃尾随空行:解析器的 end_lineno 会被紧随的空行 NL 顶到下一行
|
||||||
|
# (vast Parser._ParseSuite 单行套件分支返回前 `_SkipNL()` 吞掉空行 NL,
|
||||||
|
# 调用方 _FixLoc 取 _Prev() 即该 NL)。若不丢弃,`define f(...): return x`
|
||||||
|
# 这类内联体宏末尾会多出空白行,使下方"末行是否已有内联体"判据落空,
|
||||||
|
# 凭空补一行 ` pass` → 存根缩进非法(hashlib.__sha512 第 29 行)。
|
||||||
|
while Copied and not Copied[-1].strip():
|
||||||
|
Copied.pop()
|
||||||
|
lines.extend(Copied)
|
||||||
if lines and lines[-1].strip() and not lines[-1].rstrip().endswith(':'):
|
if lines and lines[-1].strip() and not lines[-1].rstrip().endswith(':'):
|
||||||
pass
|
pass
|
||||||
else:
|
elif lines:
|
||||||
lines.append(f'{IndentStr} pass')
|
lines.append(f'{IndentStr} pass')
|
||||||
|
else:
|
||||||
|
# 源区间为空(理论不可达):产出合法占位,绝不落出 ` pass` 这类非法缩进
|
||||||
|
lines.append(f'{IndentStr}pass')
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
# 处理函数装饰器
|
# 处理函数装饰器
|
||||||
@@ -661,7 +673,17 @@ class PythonToStubConverter:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _GetTypeString(annotation: ast.AST) -> str:
|
def _GetTypeString(annotation: ast.AST) -> str:
|
||||||
"""获取类型字符串"""
|
"""获取类型字符串
|
||||||
|
|
||||||
|
支持集与 TPV(TransPyV/App/lib/Stub/StubGen/Converter.vp 的 _PyiTypeStr)
|
||||||
|
等价:Name/Attribute/Subscript/BinOp/UnaryOp/Compare/BoolOp/Constant/
|
||||||
|
List/Call(TPV 另支持裸 Tuple,本侧仅在 Subscript.slice 位置接受 Tuple)。
|
||||||
|
未支持的节点种类与运算符在此 fail-fast(不自造 't.CVoid' 回落)。
|
||||||
|
"""
|
||||||
|
# 无注解(None):调用方未提供注解,沿用原语义返回 't.CVoid';
|
||||||
|
# 这不是"未知节点"——未知节点走函数尾 fail-fast。
|
||||||
|
if annotation is None:
|
||||||
|
return 't.CVoid'
|
||||||
if isinstance(annotation, ast.Name):
|
if isinstance(annotation, ast.Name):
|
||||||
return annotation.id
|
return annotation.id
|
||||||
elif isinstance(annotation, ast.Attribute):
|
elif isinstance(annotation, ast.Attribute):
|
||||||
@@ -706,6 +728,15 @@ class PythonToStubConverter:
|
|||||||
return f'{LeftStr} / {RightStr}'
|
return f'{LeftStr} / {RightStr}'
|
||||||
elif isinstance(annotation.op, ast.Mod):
|
elif isinstance(annotation.op, ast.Mod):
|
||||||
return f'{LeftStr} % {RightStr}'
|
return f'{LeftStr} % {RightStr}'
|
||||||
|
# 位与/右移:条件宏实参(如 c.CIf(MASK_BYTE & 0x0F)、
|
||||||
|
# c.CIf((MAX_SIZE >> SHIFT_BITS) > 0))。两运算符必须与 TPV 的
|
||||||
|
# _PyiBinOpOpText 保持一致,否则同一份源码两侧写出的 vpi 文本不同。
|
||||||
|
elif isinstance(annotation.op, ast.BitAnd):
|
||||||
|
return f'{LeftStr} & {RightStr}'
|
||||||
|
elif isinstance(annotation.op, ast.RShift):
|
||||||
|
return f'{LeftStr} >> {RightStr}'
|
||||||
|
# 其余二元运算符(LShift/BitXor/FloorDiv/Pow/MatMult 等)不支持:
|
||||||
|
# 落到函数尾 fail-fast(与 TPV 同语义)
|
||||||
elif isinstance(annotation, ast.UnaryOp):
|
elif isinstance(annotation, ast.UnaryOp):
|
||||||
if isinstance(annotation.op, ast.Not):
|
if isinstance(annotation.op, ast.Not):
|
||||||
operand: str = PythonToStubConverter._GetTypeString(annotation.operand)
|
operand: str = PythonToStubConverter._GetTypeString(annotation.operand)
|
||||||
@@ -740,6 +771,11 @@ class PythonToStubConverter:
|
|||||||
for i, op in enumerate(ops):
|
for i, op in enumerate(ops):
|
||||||
result += f' {op} {comparators[i]}'
|
result += f' {op} {comparators[i]}'
|
||||||
return result
|
return result
|
||||||
|
elif isinstance(annotation, ast.BoolOp):
|
||||||
|
# 布尔运算:条件宏实参(如 c.CIf(ENABLED and MAX_SIZE > 0))。
|
||||||
|
# 文本与 TPV 的 BoolOp 分支一致:and → ' and '、or → ' or '。
|
||||||
|
Joiner: str = ' or ' if isinstance(annotation.op, ast.Or) else ' and '
|
||||||
|
return Joiner.join([PythonToStubConverter._GetTypeString(v) for v in annotation.values])
|
||||||
elif isinstance(annotation, ast.Constant):
|
elif isinstance(annotation, ast.Constant):
|
||||||
return repr(annotation.value)
|
return repr(annotation.value)
|
||||||
elif isinstance(annotation, ast.Index):
|
elif isinstance(annotation, ast.Index):
|
||||||
@@ -765,7 +801,17 @@ class PythonToStubConverter:
|
|||||||
return f'{FuncStr}({keywords_str})'
|
return f'{FuncStr}({keywords_str})'
|
||||||
else:
|
else:
|
||||||
return f'{FuncStr}({ArgsStr})'
|
return f'{FuncStr}({ArgsStr})'
|
||||||
return 't.CVoid'
|
# 未知/未支持的注解节点 fail-fast(与 TPV StubGen 的 _PyiTypeStr 同语义、
|
||||||
|
# 同文案、同类型):静默回落 't.CVoid' 会把错误类型写进 stub 签名——实例:
|
||||||
|
# 源里的 t.Callable[[], int] 曾写成 t.Callable[int, int](见
|
||||||
|
# ViperApps/ViperOS/VKernel/.tpv_build/temp/0b/0bb176432facf634.vpi:23),
|
||||||
|
# 下游按错误签名解析且无任何提示。
|
||||||
|
# 归因信息:节点 kind + 行号;文件归因由调用方在日志里带上
|
||||||
|
# (Generator.GenerateStub → "Failed to process <文件>",
|
||||||
|
# Phase1Generator → "处理 include 文件失败 <相对路径>")。
|
||||||
|
KindName: str = type(annotation).__name__
|
||||||
|
LineNo: int = getattr(annotation, 'lineno', 0)
|
||||||
|
raise TypeError(f'vpi 类型注解节点类型未知: {KindName} (line {LineNo})')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _ExtractPostdefClass(annotation: ast.AST) -> str | None:
|
def _ExtractPostdefClass(annotation: ast.AST) -> str | None:
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
|
|||||||
from lib.core.VLogger import get_logger as _vlog
|
from lib.core.VLogger import get_logger as _vlog
|
||||||
from lib.constants.config import mode as _config_mode
|
from lib.constants.config import mode as _config_mode
|
||||||
from lib.includes import t
|
from lib.includes import t
|
||||||
from lib.core.SymbolUtils import IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext, AnnotationContainsName, IsCPtrNode
|
from lib.core.SymbolUtils import (
|
||||||
|
IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext,
|
||||||
|
AnnotationContainsName, IsCPtrNode, RequireDefineConstStorable,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _IsGenericTpName(name: str) -> bool:
|
def _IsGenericTpName(name: str) -> bool:
|
||||||
@@ -52,6 +55,26 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
Gen._store(Value, NewVar)
|
Gen._store(Value, NewVar)
|
||||||
Gen.variables[VarName] = NewVar
|
Gen.variables[VarName] = NewVar
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _RhsEndsWithVoidCall(Gen: LlvmCodeGenerator, BlockBefore: ir.Block | None, InsnStart: int) -> bool:
|
||||||
|
"""判定刚求值的带注解赋值 RHS 是否"按类型即无值"(以返回 void 的调用收尾)。
|
||||||
|
|
||||||
|
判据完全取自**实际发射的指令**(不依赖 RHS 的 AST 形态白名单):仅当求值
|
||||||
|
发生在同一基本块、且新增指令的最后一条是返回 void 的 call 时,才认为该
|
||||||
|
表达式语义上本就无值(如返回 void 的内建/库调用),此时"无值可存"合法。
|
||||||
|
其余情况——无任何指令产出(类型下标被当乘法 ⇒ 整条 RHS 未生成任何东西)
|
||||||
|
或只产出 load/gep 等非收尾指令后求值失败(属性链解析失败,实测同块新增
|
||||||
|
1 条 load)——都属编译期求值失败,必须 fail-fast。
|
||||||
|
"""
|
||||||
|
BlockAfter: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
|
||||||
|
if InsnStart < 0 or BlockAfter is None or BlockAfter is not BlockBefore:
|
||||||
|
return False
|
||||||
|
NewInstrs: list[ir.Instruction] = BlockAfter.instructions[InsnStart:]
|
||||||
|
if not NewInstrs:
|
||||||
|
return False
|
||||||
|
Last: ir.Instruction = NewInstrs[-1]
|
||||||
|
return isinstance(Last, ir.CallInstr) and isinstance(Last.type, ir.VoidType)
|
||||||
|
|
||||||
def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None:
|
def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None:
|
||||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||||
AttrName: str = Target.attr
|
AttrName: str = Target.attr
|
||||||
@@ -210,7 +233,16 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||||
Value = Gen.builder.inttoptr(Value, TargetType, name="int_to_ptr")
|
Value = Gen.builder.inttoptr(Value, TargetType, name="int_to_ptr")
|
||||||
elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.PointerType):
|
elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.PointerType):
|
||||||
Value = Gen.builder.ptrtoint(Value, TargetType, name="ptr_to_int")
|
if isinstance(Value, ir.Constant) and Value.constant is None:
|
||||||
|
# null 指针常量赋给整数字段 → 0(C 空指针常量语义)
|
||||||
|
Value = ir.Constant(TargetType, 0)
|
||||||
|
else:
|
||||||
|
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)
|
||||||
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"指针隐式转整数已禁止:不能把指针类型 {Value.type} 赋给整数类型 {TargetType};"
|
||||||
|
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
|
)
|
||||||
Gen._store(Value, Ptr)
|
Gen._store(Value, Ptr)
|
||||||
|
|
||||||
def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None:
|
def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None:
|
||||||
@@ -356,6 +388,9 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
|
|
||||||
def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None:
|
def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None:
|
||||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||||
|
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列):
|
||||||
|
# 局部变量/self 字段注解不经过 ResolveAnnotationType,故在此显式校验。
|
||||||
|
self._CheckFixedArrayKeyword(Node.annotation)
|
||||||
# M1 协程:记录 async 函数调用赋值的变量名(coro.coro_run 内联翻译需要)
|
# M1 协程:记录 async 函数调用赋值的变量名(coro.coro_run 内联翻译需要)
|
||||||
if isinstance(Node.target, ast.Name) and Node.value is not None:
|
if isinstance(Node.target, ast.Name) and Node.value is not None:
|
||||||
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
|
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
|
||||||
@@ -373,6 +408,10 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
IsPtr: bool = TypeInfo.IsPtr
|
IsPtr: bool = TypeInfo.IsPtr
|
||||||
if TypeInfo.IsDefine:
|
if TypeInfo.IsDefine:
|
||||||
if Node.value and isinstance(Node.value, ast.Constant):
|
if Node.value and isinstance(Node.value, ast.Constant):
|
||||||
|
# fail-fast:常量值必须能落进声明类型(define 带类型形态)/ 64 位存储
|
||||||
|
# (自动推导形态)。唯一判据见 SymbolUtils.RequireDefineIntStorable。
|
||||||
|
RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType,
|
||||||
|
Node.annotation, Node.value.value, VarName)
|
||||||
if not hasattr(Gen, '_define_constants'):
|
if not hasattr(Gen, '_define_constants'):
|
||||||
Gen._define_constants = {}
|
Gen._define_constants = {}
|
||||||
Gen._define_constants[VarName] = Node.value.value
|
Gen._define_constants[VarName] = Node.value.value
|
||||||
@@ -387,6 +426,8 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
ctx = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans)
|
ctx = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans)
|
||||||
eval_val = ConstEvaluator.eval_full(Node.value, ctx)
|
eval_val = ConstEvaluator.eval_full(Node.value, ctx)
|
||||||
if eval_val is not None and isinstance(eval_val, (int, float)):
|
if eval_val is not None and isinstance(eval_val, (int, float)):
|
||||||
|
RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType,
|
||||||
|
Node.annotation, eval_val, VarName)
|
||||||
if not hasattr(Gen, '_define_constants'):
|
if not hasattr(Gen, '_define_constants'):
|
||||||
Gen._define_constants = {}
|
Gen._define_constants = {}
|
||||||
Gen._define_constants[VarName] = eval_val
|
Gen._define_constants[VarName] = eval_val
|
||||||
@@ -491,7 +532,13 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
and 'list' in self.Trans.ClassHandler._generic_class_templates):
|
and 'list' in self.Trans.ClassHandler._generic_class_templates):
|
||||||
self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub)
|
self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub)
|
||||||
return
|
return
|
||||||
if TypeInfo.IsVoid and isinstance(Node.annotation, ast.BinOp):
|
# Only fall back to the left operand when the union neither resolved a base
|
||||||
|
# type nor accumulated multi-level pointers. A pointer-marker based union
|
||||||
|
# (`t.CPtr*` = `t.CPtr | t.CPtr` => CVoid/2) is already a fully resolved
|
||||||
|
# multi-level pointer; falling back to its left operand (`t.CPtr` => 1 layer)
|
||||||
|
# used to silently degrade it to i8*. Keep PtrCount > 1 untouched.
|
||||||
|
if (TypeInfo.IsVoid and TypeInfo.PtrCount <= 1
|
||||||
|
and isinstance(Node.annotation, ast.BinOp)):
|
||||||
found: str | None = FindStructNameInAnnotationWithContext(
|
found: str | None = FindStructNameInAnnotationWithContext(
|
||||||
Node.annotation, self.Trans, Gen)
|
Node.annotation, self.Trans, Gen)
|
||||||
if found:
|
if found:
|
||||||
@@ -513,6 +560,11 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
and Node.annotation.id not in Gen.class_vtable):
|
and Node.annotation.id not in Gen.class_vtable):
|
||||||
TypeInfo.PtrCount = 0
|
TypeInfo.PtrCount = 0
|
||||||
IsPtr = False
|
IsPtr = False
|
||||||
|
# const / volatile 局部变量登记(统一登记入口)。放在此处:TypeInfo 的
|
||||||
|
# PtrCount/IsStruct 已定稿(上方 526-547),且早于本函数所有后续分支与
|
||||||
|
# 提前 return(数组/list/_reg_values/已存在 alloca 等),保证「局部变量」
|
||||||
|
# 这一登记点只有一处、无遗漏。
|
||||||
|
Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=False)
|
||||||
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
|
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
|
||||||
if not IsPtr and TypeInfo.IsStruct and TypeInfo.Name:
|
if not IsPtr and TypeInfo.IsStruct and TypeInfo.Name:
|
||||||
stripped: str = TypeInfo.Name
|
stripped: str = TypeInfo.Name
|
||||||
@@ -653,11 +705,13 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
elif isinstance(Node.value, ast.Attribute):
|
elif isinstance(Node.value, ast.Attribute):
|
||||||
# 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp
|
# 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp
|
||||||
# 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。
|
# 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。
|
||||||
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen)
|
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName)
|
||||||
Gen._record_var_signedness(VarName, IsUnsigned)
|
Gen._record_var_signedness(VarName, IsUnsigned)
|
||||||
return
|
return
|
||||||
del Gen._reg_values[VarName]
|
del Gen._reg_values[VarName]
|
||||||
InitValue: ir.Value | None = None
|
InitValue: ir.Value | None = None
|
||||||
|
RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
|
||||||
|
RhsInsnStart: int = len(RhsBlock.instructions) if RhsBlock is not None else -1
|
||||||
if Node.value:
|
if Node.value:
|
||||||
InitValue = self.HandleExprLlvm(Node.value, VarType=VarType)
|
InitValue = self.HandleExprLlvm(Node.value, VarType=VarType)
|
||||||
# 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型
|
# 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型
|
||||||
@@ -665,10 +719,14 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
CalledName: str = Node.value.func.id
|
CalledName: str = Node.value.func.id
|
||||||
if CalledName in Gen._closure_return_types:
|
if CalledName in Gen._closure_return_types:
|
||||||
Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName]
|
Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName]
|
||||||
if InitValue is None and Node.value is not None and isinstance(Node.value, ast.Attribute):
|
if InitValue is None and Node.value is not None:
|
||||||
# 防静默:带值带注解的属性链求值失败(返回 None)必须报编译错误,
|
# 防静默(通用判据,不再只认 ast.Attribute):带值带注解赋值 RHS
|
||||||
# 否则变量 alloca 已创建却无 store → 后续 load 未初始化栈变量。
|
# 求值为 None 且非"按类型即无值"(未以返回 void 的调用收尾)时,
|
||||||
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen)
|
# 变量 alloca 已创建却无 store → 后续 load 读未初始化栈变量
|
||||||
|
# (运行期随机崩溃;如类型下标被当乘法的 list[X*]*(...) 整条 RHS
|
||||||
|
# 不产任何指令)。判据取自实际发射的指令,见 _RhsEndsWithVoidCall。
|
||||||
|
if not self._RhsEndsWithVoidCall(Gen, RhsBlock, RhsInsnStart):
|
||||||
|
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName)
|
||||||
if VarName in Gen._reg_values:
|
if VarName in Gen._reg_values:
|
||||||
del Gen._reg_values[VarName]
|
del Gen._reg_values[VarName]
|
||||||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||||||
@@ -844,11 +902,21 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
elif isinstance(VarType, ir.PointerType) and isinstance(InitValue.type, ir.IntType):
|
elif isinstance(VarType, ir.PointerType) and isinstance(InitValue.type, ir.IntType):
|
||||||
InitValue = Gen.builder.inttoptr(InitValue, VarType, name=f"int2ptr_{VarName}")
|
InitValue = Gen.builder.inttoptr(InitValue, VarType, name=f"int2ptr_{VarName}")
|
||||||
elif isinstance(VarType, ir.IntType) and isinstance(InitValue.type, ir.PointerType):
|
elif isinstance(VarType, ir.IntType) and isinstance(InitValue.type, ir.PointerType):
|
||||||
InitValue = Gen.builder.ptrtoint(InitValue, VarType, name=f"ptr2int_{VarName}")
|
if isinstance(InitValue, ir.Constant) and InitValue.constant is None:
|
||||||
|
# null 指针常量初始化整数变量 → 0(C 空指针常量语义)
|
||||||
|
InitValue = ir.Constant(VarType, 0)
|
||||||
|
else:
|
||||||
|
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)
|
||||||
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"指针隐式转整数已禁止:不能用指针类型 {InitValue.type} 初始化整数变量 "
|
||||||
|
f"{VarName}: {VarType};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
|
)
|
||||||
# 大端局部变量:存储时 bswap
|
# 大端局部变量:存储时 bswap
|
||||||
InitValue = Gen._apply_bswap_if_big(InitValue, TypeInfo.ByteOrder, f"bswap_store_{VarName}")
|
InitValue = Gen._apply_bswap_if_big(InitValue, TypeInfo.ByteOrder, f"bswap_store_{VarName}")
|
||||||
Gen._store(InitValue, var)
|
Gen._store(InitValue, var)
|
||||||
except Exception as _e:
|
except (TypeError, ValueError) as _e:
|
||||||
|
# 仅回退 llvmlite 自身的类型/值错误;FailFastError 等一律上抛(不吞)
|
||||||
if _config_mode == "strict":
|
if _config_mode == "strict":
|
||||||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||||||
Gen.variables[VarName] = var
|
Gen.variables[VarName] = var
|
||||||
@@ -955,12 +1023,15 @@ class AnnAssignHandle(BaseHandle):
|
|||||||
if _config_mode == "strict":
|
if _config_mode == "strict":
|
||||||
raise
|
raise
|
||||||
_vlog().warning(f"解析类型失败: {_e}", "Exception")
|
_vlog().warning(f"解析类型失败: {_e}", "Exception")
|
||||||
|
_RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
|
||||||
|
_RhsInsnStart: int = len(_RhsBlock.instructions) if _RhsBlock is not None else -1
|
||||||
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=AttrVarType)
|
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=AttrVarType)
|
||||||
if Value:
|
if Value:
|
||||||
self._HandleAttributeStoreLlvm(Node.target, Value)
|
self._HandleAttributeStoreLlvm(Node.target, Value)
|
||||||
elif isinstance(Node.value, ast.Attribute):
|
elif not self._RhsEndsWithVoidCall(Gen, _RhsBlock, _RhsInsnStart):
|
||||||
# 防静默:self 字段带注解赋值右侧属性链求值失败必须报错
|
# 防静默(通用判据,不再只认 ast.Attribute):self 字段带注解
|
||||||
self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen)
|
# 赋值右侧求值失败(None)必须报错,否则字段写被静默跳过。
|
||||||
|
self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen, Node.annotation, Node.target.attr)
|
||||||
if Value:
|
if Value:
|
||||||
AttrName: str = Node.target.attr
|
AttrName: str = Node.target.attr
|
||||||
len_name: str = f'{AttrName}__len'
|
len_name: str = f'{AttrName}__len'
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ class AssignHandle(BaseHandle):
|
|||||||
return any(AssignHandle._contains_identified_struct(e) for e in var_type.elements)
|
return any(AssignHandle._contains_identified_struct(e) for e in var_type.elements)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None) -> ir.Value | None:
|
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None,
|
||||||
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
|
ConstWidthHint: int | None = None) -> ir.Value | None:
|
||||||
|
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
|
||||||
|
|
||||||
def _HandleAssignLlvm(self, Node: ast.Assign) -> None:
|
def _HandleAssignLlvm(self, Node: ast.Assign) -> None:
|
||||||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||||
@@ -41,9 +42,8 @@ class AssignHandle(BaseHandle):
|
|||||||
Target: ast.expr = Node.targets[0]
|
Target: ast.expr = Node.targets[0]
|
||||||
if isinstance(Target, ast.Name):
|
if isinstance(Target, ast.Name):
|
||||||
VarName: str = Target.id
|
VarName: str = Target.id
|
||||||
if VarName in Gen.var_const_flags:
|
if Gen._IsVarConst(VarName):
|
||||||
src_info: str = Gen._get_node_info()
|
_vlog().error(Gen._ConstAssignMessage("赋值", VarName))
|
||||||
_vlog().error(f"不能对 const 变量 '{VarName}' 赋值{src_info}")
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) and IsTModule(Node.value.value.id, self.Trans.SymbolTable):
|
if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) and IsTModule(Node.value.value.id, self.Trans.SymbolTable):
|
||||||
AttrName: str = Node.value.attr
|
AttrName: str = Node.value.attr
|
||||||
@@ -698,8 +698,9 @@ class AssignHandle(BaseHandle):
|
|||||||
val: ir.Value | None = self.HandleExprLlvm(kw.value)
|
val: ir.Value | None = self.HandleExprLlvm(kw.value)
|
||||||
if val:
|
if val:
|
||||||
member_values[kw.arg] = val
|
member_values[kw.arg] = val
|
||||||
has_vtable: bool = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
|
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源),
|
||||||
base: int = 1 if has_vtable else 0
|
# 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。
|
||||||
|
base: int = Gen._member_base(ClassName)
|
||||||
for i, (member_name, member_type) in enumerate(members):
|
for i, (member_name, member_type) in enumerate(members):
|
||||||
if isinstance(member_type, ir.VoidType):
|
if isinstance(member_type, ir.VoidType):
|
||||||
continue
|
continue
|
||||||
@@ -719,6 +720,15 @@ class AssignHandle(BaseHandle):
|
|||||||
def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None:
|
def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None:
|
||||||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||||
AttrName: str = Target.attr
|
AttrName: str = Target.attr
|
||||||
|
# const 类字段:给 `obj.字段 = ...` / `self.字段 = ...` 直接赋值同样拒绝
|
||||||
|
# (判据 = 字段自身 const,见 BaseGen._IsFieldConst / Debugger/type-descriptors.md)。
|
||||||
|
# 类名能解析时按 `类名.字段名` 精确判定;解析不到时退化裸名(保守报错)。
|
||||||
|
_TargetCN: str | None = None
|
||||||
|
if isinstance(Target.value, ast.Name):
|
||||||
|
_TargetCN = self.Trans.ExprHandler._get_var_class(Target.value, Gen)
|
||||||
|
if Gen._IsFieldConst(_TargetCN, AttrName):
|
||||||
|
_vlog().error(Gen._ConstAssignMessage("字段赋值", AttrName))
|
||||||
|
sys.exit(1)
|
||||||
if isinstance(Target.value, ast.Name) and Target.value.id == 'self':
|
if isinstance(Target.value, ast.Name) and Target.value.id == 'self':
|
||||||
ClassName: str | None = self.Trans._CurrentCpythonObjectClass
|
ClassName: str | None = self.Trans._CurrentCpythonObjectClass
|
||||||
if ClassName and ClassName in Gen.structs:
|
if ClassName and ClassName in Gen.structs:
|
||||||
@@ -1256,15 +1266,20 @@ class AssignHandle(BaseHandle):
|
|||||||
|
|
||||||
def _HandleSubscriptStoreLlvm(self, Target: ast.Subscript, Value: ir.Value) -> None:
|
def _HandleSubscriptStoreLlvm(self, Target: ast.Subscript, Value: ir.Value) -> None:
|
||||||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||||
|
# pointee-volatile 基址名后缀(与 HandlesExprAttr._HandleSubscriptLlvm 同款);
|
||||||
|
# 空后缀 ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。
|
||||||
|
_PVSuffix: str = Gen._SubscriptSuffix(Target.value)
|
||||||
# 查询数组元素字节序
|
# 查询数组元素字节序
|
||||||
arr_byte_order: str = ''
|
arr_byte_order: str = ''
|
||||||
if isinstance(Target.value, ast.Name) and Target.value.id in Gen.local_var_byteorders:
|
if isinstance(Target.value, ast.Name) and Target.value.id in Gen.local_var_byteorders:
|
||||||
arr_byte_order = Gen.local_var_byteorders[Target.value.id]
|
arr_byte_order = Gen.local_var_byteorders[Target.value.id]
|
||||||
if isinstance(Target.value, ast.Name):
|
if isinstance(Target.value, ast.Name):
|
||||||
SubVarName: str = Target.value.id
|
SubVarName: str = Target.value.id
|
||||||
if SubVarName in Gen.var_const_flags:
|
if Gen._IsVarConst(SubVarName):
|
||||||
src_info: str = Gen._get_node_info()
|
# 与 Name 赋值/增量赋值同一条 fail-fast(原先此处 LogError 后继续,
|
||||||
self.Trans.LogError(f"不能对 const 变量 '{SubVarName}' 的元素赋值{src_info}")
|
# 属静默继续;现统一 error + exit,判据与报文两侧一致)。
|
||||||
|
_vlog().error(Gen._ConstAssignMessage("的元素赋值", SubVarName))
|
||||||
|
sys.exit(1)
|
||||||
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Target.value, Gen)
|
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Target.value, Gen)
|
||||||
if ClassName and Gen._has_function(f'{ClassName}.__setitem__'):
|
if ClassName and Gen._has_function(f'{ClassName}.__setitem__'):
|
||||||
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
|
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
|
||||||
@@ -1306,7 +1321,7 @@ class AssignHandle(BaseHandle):
|
|||||||
PtrToStruct: ir.Type = Value.type.pointee
|
PtrToStruct: ir.Type = Value.type.pointee
|
||||||
BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal,
|
BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal,
|
||||||
ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp")
|
ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp")
|
||||||
ElemPtr: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name="subscript")
|
ElemPtr: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
Gen.builder.store(Value, ElemPtr)
|
Gen.builder.store(Value, ElemPtr)
|
||||||
return
|
return
|
||||||
if BaseHandle._is_char_pointer(IndexVal):
|
if BaseHandle._is_char_pointer(IndexVal):
|
||||||
@@ -1324,7 +1339,7 @@ class AssignHandle(BaseHandle):
|
|||||||
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
|
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store")
|
||||||
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
|
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
||||||
return
|
return
|
||||||
if isinstance(ValueVal.type, ir.PointerType):
|
if isinstance(ValueVal.type, ir.PointerType):
|
||||||
@@ -1336,7 +1351,7 @@ class AssignHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript")
|
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
elif (isinstance(pointee, ir.IntType) and pointee.width == 8
|
elif (isinstance(pointee, ir.IntType) and pointee.width == 8
|
||||||
and isinstance(Target.value, ast.Name)
|
and isinstance(Target.value, ast.Name)
|
||||||
and (Gen.var_ptr_element.get(Target.value.id, False)
|
and (Gen.var_ptr_element.get(Target.value.id, False)
|
||||||
@@ -1346,11 +1361,17 @@ class AssignHandle(BaseHandle):
|
|||||||
CastedPtrPtr: ir.Value = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_store")
|
CastedPtrPtr: ir.Value = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_store")
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_store")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_store")
|
||||||
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
|
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
|
# fail-fast:rawptr[T](基座是指针存储)的元素为对象 T、而 RHS 是指向 T
|
||||||
|
# 的指针时,下游 _StoreWithCoerce/_coerce_value 会静默 load 整对象再按
|
||||||
|
# sizeof(T) 存储(写 sizeof(T) 而非 8 字节指针)。数组槽(t.CArray[T,N])
|
||||||
|
# 本就按 sizeof(T) 布局,整对象赋值合法 ⇒ 由 IsArrayBase 排除。
|
||||||
|
self._RejectWholeObjectCopyIntoPtrSlot(
|
||||||
|
Target, Value, ElemPtr, isinstance(pointee, ir.ArrayType))
|
||||||
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
||||||
elif isinstance(ValueVal.type, ir.ArrayType):
|
elif isinstance(ValueVal.type, ir.ArrayType):
|
||||||
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
|
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
|
||||||
@@ -1364,7 +1385,7 @@ class AssignHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr: ir.Value = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript")
|
ElemPtr: ir.Value = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
||||||
return
|
return
|
||||||
if isinstance(Target.value, ast.Subscript):
|
if isinstance(Target.value, ast.Subscript):
|
||||||
@@ -1375,7 +1396,7 @@ class AssignHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr: ir.Value = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript")
|
ElemPtr: ir.Value = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
||||||
return
|
return
|
||||||
if isinstance(Target.value, ast.Attribute):
|
if isinstance(Target.value, ast.Attribute):
|
||||||
@@ -1386,12 +1407,12 @@ class AssignHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr: ir.Value = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript")
|
ElemPtr: ir.Value = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
||||||
return
|
return
|
||||||
arr_alloc: ir.AllocaInstr = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
arr_alloc: ir.AllocaInstr = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
||||||
Gen._store(ValueVal, arr_alloc)
|
Gen._store(ValueVal, arr_alloc)
|
||||||
ElemPtr: ir.Value = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript")
|
ElemPtr: ir.Value = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
|
||||||
ModifiedArr: ir.Value = Gen._load(arr_alloc, name="modified_arr")
|
ModifiedArr: ir.Value = Gen._load(arr_alloc, name="modified_arr")
|
||||||
if isinstance(Target.value, ast.Subscript):
|
if isinstance(Target.value, ast.Subscript):
|
||||||
@@ -1403,6 +1424,71 @@ class AssignHandle(BaseHandle):
|
|||||||
if OuterPtr:
|
if OuterPtr:
|
||||||
Gen._store(ModifiedArr, OuterPtr)
|
Gen._store(ModifiedArr, OuterPtr)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _StructTypeKey(ty: ir.Type) -> str:
|
||||||
|
"""结构体类型的稳定标识(identified 用 name,literal 用类型文本)"""
|
||||||
|
if isinstance(ty, ir.IdentifiedStructType):
|
||||||
|
return ty.name
|
||||||
|
return str(ty)
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# _RejectWholeObjectCopyIntoPtrSlot - 下标赋值的"整对象拷贝"fail-fast
|
||||||
|
#
|
||||||
|
# 目标形态(唯一判据):赋值左值是 rawptr[T] 下标(元素 = 对象/结构体 T,
|
||||||
|
# 基座为指针存储),而 RHS 的值类型是指向 T 的指针。此时 _StoreWithCoerce /
|
||||||
|
# _coerce_value 的"指针 → 非指针值: load 解引用"分支会生成
|
||||||
|
# %v = load %T, %T* %rhs
|
||||||
|
# store %T %v, %T* %elem (写 sizeof(T) 字节)
|
||||||
|
# 而该槽只该写 8 字节指针(槽是按 8 字节排布的缓冲/空闲链表)⇒ 每个元素
|
||||||
|
# 越界 sizeof(T)-8 字节,写穿相邻块(MemBuddy 空闲链表被写穿的静默降级根因)。
|
||||||
|
#
|
||||||
|
# 不拦(判据刻意收窄,避免误伤):
|
||||||
|
# · 数组槽 t.CArray[T,N](IsArrayBase=True):元素槽本就按 sizeof(T) 布局,
|
||||||
|
# `v[i] = T(...)`(构造函数返回临时对象地址)是合法的整对象复制;
|
||||||
|
# · 元素是标量(rawptr[int]):ElemPtr 元素不是结构体;
|
||||||
|
# · 元素本身是指针(rawptr[rawptr[T]] / X|t.CPtr):元素类型是 Ptr;
|
||||||
|
# · RHS 所指类型与元素类型不一致(非本形态)。
|
||||||
|
# 与 TPV AssignGenMixin._reject_whole_object_copy_into_ptr_slot 同口径。
|
||||||
|
# ============================================================
|
||||||
|
def _RejectWholeObjectCopyIntoPtrSlot(self, Target: ast.expr, Value: ir.Value,
|
||||||
|
ElemPtr: ir.Value, IsArrayBase: bool) -> None:
|
||||||
|
"""rawptr[T] 下标赋值 + RHS 为指向 T 的指针 ⇒ fail-fast(整对象拷贝)"""
|
||||||
|
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||||
|
if IsArrayBase:
|
||||||
|
return
|
||||||
|
if ElemPtr is None or not isinstance(ElemPtr.type, ir.PointerType):
|
||||||
|
return
|
||||||
|
ElemTy: ir.Type = ElemPtr.type.pointee
|
||||||
|
if not isinstance(ElemTy, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
|
return
|
||||||
|
if Value is None or not isinstance(Value.type, ir.PointerType):
|
||||||
|
return
|
||||||
|
RhsInner: ir.Type = Value.type.pointee
|
||||||
|
if not isinstance(RhsInner, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
|
return
|
||||||
|
ElemKey: str = AssignHandle._StructTypeKey(ElemTy)
|
||||||
|
RhsKey: str = AssignHandle._StructTypeKey(RhsInner)
|
||||||
|
if ElemKey is None or RhsKey is None:
|
||||||
|
return
|
||||||
|
if ElemKey != RhsKey:
|
||||||
|
return
|
||||||
|
ElemSize: int = Gen._get_struct_size(ElemTy)
|
||||||
|
PtrSize: int = 8
|
||||||
|
Over: int = ElemSize - PtrSize
|
||||||
|
if Over < 0:
|
||||||
|
Over = 0
|
||||||
|
SrcInfo: str = Gen._get_node_info()
|
||||||
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"下标赋值的左值元素是对象 '{ElemKey}'({ElemSize} 字节),"
|
||||||
|
f"右值却是指向 '{RhsKey}' 的指针:编译器会静默生成整对象拷贝"
|
||||||
|
f"(load {ElemKey} + store {ElemKey},写 {ElemSize} 字节),"
|
||||||
|
f"而不是按指针写 {PtrSize} 字节。若该地址实际按 8 字节槽存放指针"
|
||||||
|
f"(如 rawptr[T] 缓冲/空闲链表),每写一个元素就越界 {Over} 字节,"
|
||||||
|
f"直接写穿相邻块。修法:把指针槽的基座类型写成指针元素形态"
|
||||||
|
f"(`rawptr[T] | t.CPtr` 或 `rawptr[rawptr[T]]`)后再赋值;"
|
||||||
|
f"或把基座显式转为 t.CPtr(无类型指针)后按 8 字节指针语义写入。{SrcInfo}")
|
||||||
|
|
||||||
def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value) -> None:
|
def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value) -> None:
|
||||||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||||
if isinstance(Ptr.type, ir.PointerType):
|
if isinstance(Ptr.type, ir.PointerType):
|
||||||
@@ -1456,12 +1542,21 @@ class AssignHandle(BaseHandle):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None:
|
def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None:
|
||||||
|
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列):
|
||||||
|
# 模块级全局注解不经过 ResolveAnnotationType,故在此显式校验。
|
||||||
|
self._CheckFixedArrayKeyword(Node.annotation)
|
||||||
if isinstance(Node.target, ast.Name):
|
if isinstance(Node.target, ast.Name):
|
||||||
VarName: str = Node.target.id
|
VarName: str = Node.target.id
|
||||||
Info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName)
|
Info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName)
|
||||||
if Info:
|
if Info:
|
||||||
if Info.IsTypedef:
|
if Info.IsTypedef:
|
||||||
return
|
return
|
||||||
|
# 模块级全局 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`):
|
||||||
|
# 先解析(空/缺段名、非正整数字面量 aligned → fail-fast 终止),再在各
|
||||||
|
# 全局创建点用 _ApplyGlobalAttrs 施加。此前 TPC 全局路径完全没有
|
||||||
|
# section/align/weak 代码(函数侧 section 只来自装饰器,见
|
||||||
|
# HandlesFunctions.py:1361-1362 → :1767)。
|
||||||
|
GlobalAttrs: dict[str, Any] = self._ExtractGlobalAttrs(Node, Gen)
|
||||||
parse_result = ParseListAnnotation(Node.annotation)
|
parse_result = ParseListAnnotation(Node.annotation)
|
||||||
if parse_result:
|
if parse_result:
|
||||||
elem_type_node: ast.expr = parse_result.elem_type_node
|
elem_type_node: ast.expr = parse_result.elem_type_node
|
||||||
@@ -1470,15 +1565,30 @@ class AssignHandle(BaseHandle):
|
|||||||
check_func_ptr=True, void_fallback_width=32, fallback_to_cint=True,
|
check_func_ptr=True, void_fallback_width=32, fallback_to_cint=True,
|
||||||
)
|
)
|
||||||
if parse_result.is_pointer:
|
if parse_result.is_pointer:
|
||||||
# 单参数 list[type] → 指针
|
# 单参数 t.CArray[T]:与 t.CArray[T, None] 统一为"长度自动推导数组"。
|
||||||
VarType: ir.Type = ir.PointerType(ElemType)
|
# 有可推导初值(列表字面量 / 字符串字面量)时不再生成裸指针,而是落入
|
||||||
if VarName not in Gen.variables and VarName not in Gen.module.globals:
|
# 下方数组路径:由 ParseArrayCount(count_node=None) 从字面量推导长度、
|
||||||
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
# 字节内联到变量自身(与类成员 DeclarationGenerator.py:775-784、
|
||||||
GlobalVar.initializer = ir.Constant(VarType, None)
|
# 局部 HandlesAnnAssign.py:446-489 同一口径;用户语义:单参数即
|
||||||
Gen.variables[VarName] = GlobalVar
|
# "长度自动推导",不再是隐式指针)。
|
||||||
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
# 无可推导初值(纯声明 / 真正的指针用法)时保持既有指针语义逐字不变。
|
||||||
Gen.global_struct_class[VarName] = elem_type_node.id
|
HasDerivableInit: bool = bool(
|
||||||
return
|
Node.value and (
|
||||||
|
isinstance(Node.value, ast.List)
|
||||||
|
or (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not HasDerivableInit:
|
||||||
|
VarType: ir.Type = ir.PointerType(ElemType)
|
||||||
|
if VarName not in Gen.variables and VarName not in Gen.module.globals:
|
||||||
|
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
||||||
|
GlobalVar.initializer = ir.Constant(VarType, None)
|
||||||
|
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
|
||||||
|
Gen.variables[VarName] = GlobalVar
|
||||||
|
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
||||||
|
Gen.global_struct_class[VarName] = elem_type_node.id
|
||||||
|
return
|
||||||
|
# 有初值 → 落入下方数组路径(count_node=None → 从字面量推导长度)
|
||||||
count_node: ast.expr | None = parse_result.count_node
|
count_node: ast.expr | None = parse_result.count_node
|
||||||
ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='global')
|
ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='global')
|
||||||
VarType = ir.ArrayType(ElemType, ArrayCount)
|
VarType = ir.ArrayType(ElemType, ArrayCount)
|
||||||
@@ -1506,6 +1616,7 @@ class AssignHandle(BaseHandle):
|
|||||||
GlobalVar.linkage = 'common'
|
GlobalVar.linkage = 'common'
|
||||||
else:
|
else:
|
||||||
GlobalVar.initializer = ir.Constant(VarType, [ir.Constant(ElemType, ir.Undefined)] * ArrayCount)
|
GlobalVar.initializer = ir.Constant(VarType, [ir.Constant(ElemType, ir.Undefined)] * ArrayCount)
|
||||||
|
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
|
||||||
Gen.variables[VarName] = GlobalVar
|
Gen.variables[VarName] = GlobalVar
|
||||||
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
||||||
Gen.global_struct_class[VarName] = elem_type_node.id
|
Gen.global_struct_class[VarName] = elem_type_node.id
|
||||||
@@ -1523,6 +1634,7 @@ class AssignHandle(BaseHandle):
|
|||||||
GlobalVar.initializer = ir.Constant(VarType, None)
|
GlobalVar.initializer = ir.Constant(VarType, None)
|
||||||
else:
|
else:
|
||||||
GlobalVar.initializer = ir.Constant(VarType, None)
|
GlobalVar.initializer = ir.Constant(VarType, None)
|
||||||
|
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
|
||||||
Gen.variables[VarName] = GlobalVar
|
Gen.variables[VarName] = GlobalVar
|
||||||
Gen.global_struct_class[VarName] = StructName
|
Gen.global_struct_class[VarName] = StructName
|
||||||
return
|
return
|
||||||
@@ -1557,6 +1669,12 @@ class AssignHandle(BaseHandle):
|
|||||||
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
|
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
|
||||||
if isinstance(VarType, ir.VoidType):
|
if isinstance(VarType, ir.VoidType):
|
||||||
VarType = ir.IntType(32)
|
VarType = ir.IntType(32)
|
||||||
|
# const / volatile 模块级全局登记(统一登记入口,持久层 global_*)。
|
||||||
|
# 局限(明确记录,见 Debugger/type-descriptors.md 已知差异):本分支只覆盖
|
||||||
|
# 标量/指针型全局;数组(t.CArray[...])与结构体型全局在更早的分支
|
||||||
|
# (上方 1568-1627)已 return,不在此登记 —— 全仓普查无这类带 const/
|
||||||
|
# volatile 注解的全局,故无可观测差异。
|
||||||
|
Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=True)
|
||||||
# 记录模块级注解类型:供函数内 global 变量的 += (HandlesAugAssign)参考,
|
# 记录模块级注解类型:供函数内 global 变量的 += (HandlesAugAssign)参考,
|
||||||
# 避免用 RHS 字面量类型(i32)误建 i32 全局导致 i64/i32 类型不匹配。
|
# 避免用 RHS 字面量类型(i32)误建 i32 全局导致 i64/i32 类型不匹配。
|
||||||
if not hasattr(Gen, '_module_ann_types'):
|
if not hasattr(Gen, '_module_ann_types'):
|
||||||
@@ -1593,6 +1711,7 @@ class AssignHandle(BaseHandle):
|
|||||||
if isinstance(VarType, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
if isinstance(VarType, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
||||||
GlobalVar.initializer = ir.Constant(VarType, None)
|
GlobalVar.initializer = ir.Constant(VarType, None)
|
||||||
|
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
|
||||||
Gen.variables[VarName] = GlobalVar
|
Gen.variables[VarName] = GlobalVar
|
||||||
if TypeInfo and TypeInfo.IsStruct and TypeInfo.Name:
|
if TypeInfo and TypeInfo.IsStruct and TypeInfo.Name:
|
||||||
Gen.global_struct_class[VarName] = TypeInfo.Name
|
Gen.global_struct_class[VarName] = TypeInfo.Name
|
||||||
@@ -1666,8 +1785,159 @@ class AssignHandle(BaseHandle):
|
|||||||
GlobalVar.linkage = 'common'
|
GlobalVar.linkage = 'common'
|
||||||
else:
|
else:
|
||||||
GlobalVar.initializer = Gen._zero_value_for_type(VarType)
|
GlobalVar.initializer = Gen._zero_value_for_type(VarType)
|
||||||
|
# 属性最后施加:weak 需覆盖分支内可能设过的 'common'(对齐 TransPyV
|
||||||
|
# TranslatorGenMixin.vp:926-929 先清 Linkage 再设 weak 的语义)。
|
||||||
|
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
|
||||||
Gen.variables[VarName] = GlobalVar
|
Gen.variables[VarName] = GlobalVar
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# 模块级全局的 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`)
|
||||||
|
#
|
||||||
|
# 镜像 TransPyV(Viper 自举侧)已有实现:
|
||||||
|
# TranslatorGenMixin._scan_global_tattr / _extract_global_attr_section /
|
||||||
|
# _extract_global_attr_aligned / _extract_global_attr_weak(.vp:590-651)
|
||||||
|
# + ClassGenBase._tattr_name(.vp:341-358)+ _class_has_tattr 的
|
||||||
|
# `c.Attribute(t.attr.xxx)` 解包(.vp:359-394)。
|
||||||
|
# TPC(Python 侧)此前完全没有全局 section/align/weak 代码。
|
||||||
|
# ======================================================================
|
||||||
|
def _TAttrName(self, node: ast.AST | None) -> str | None:
|
||||||
|
"""从 t.attr.<name> / t.attr.<name>(args) 节点提取最外层属性名。
|
||||||
|
|
||||||
|
对应 TransPyV ClassGenBase._tattr_name:Call → 递归取 func;
|
||||||
|
Attribute → 取 attr。
|
||||||
|
"""
|
||||||
|
if isinstance(node, ast.Call):
|
||||||
|
return self._TAttrName(node.func)
|
||||||
|
if isinstance(node, ast.Attribute):
|
||||||
|
return node.attr
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _ScanGlobalTAttr(self, node: ast.AST | None, want_name: str) -> ast.AST | None:
|
||||||
|
"""在全局变量注解中查找 t.attr.<want_name> 节点,命中返回该节点。
|
||||||
|
|
||||||
|
注解形态是 BinOp(BitOr) 左深链(`A | B | C` = BinOp(BinOp(A,B),C)),
|
||||||
|
递归左右子节点。命中节点形态:Call(t.attr.aligned(16))或
|
||||||
|
Attribute(t.attr.weak)。同时兼容包装写法
|
||||||
|
`T | c.Attribute(t.attr.section("..."))`(解包参考 TransPyV
|
||||||
|
ClassGenBase._class_has_tattr,.vp:375-389)。
|
||||||
|
"""
|
||||||
|
if node is None:
|
||||||
|
return None
|
||||||
|
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
|
||||||
|
hit_left: ast.AST | None = self._ScanGlobalTAttr(node.left, want_name)
|
||||||
|
if hit_left is not None:
|
||||||
|
return hit_left
|
||||||
|
return self._ScanGlobalTAttr(node.right, want_name)
|
||||||
|
# 包装写法:c.Attribute(t.attr.xxx / t.attr.xxx(...)) → 拆 args 再扫
|
||||||
|
# (与 HandlesFunctions.py:1354 装饰器侧同一判据:func.attr=='Attribute'
|
||||||
|
# 且 func.value 为 Name('c'))
|
||||||
|
if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)
|
||||||
|
and node.func.attr == 'Attribute'
|
||||||
|
and isinstance(node.func.value, ast.Name) and node.func.value.id == 'c'):
|
||||||
|
for arg in node.args:
|
||||||
|
hit_arg: ast.AST | None = self._ScanGlobalTAttr(arg, want_name)
|
||||||
|
if hit_arg is not None:
|
||||||
|
return hit_arg
|
||||||
|
return None
|
||||||
|
if self._TAttrName(node) == want_name:
|
||||||
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _TAttrArgStr(node: ast.AST | None) -> str | None:
|
||||||
|
"""取 t.attr.<name>("...") 的字符串字面量参数(缺参/非字面量返回 None)。"""
|
||||||
|
if isinstance(node, ast.Call) and node.args:
|
||||||
|
arg0: ast.AST = node.args[0]
|
||||||
|
if isinstance(arg0, ast.Constant) and isinstance(arg0.value, str):
|
||||||
|
return arg0.value
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _TAttrAlignInt(node: ast.AST | None) -> int | None:
|
||||||
|
"""取 t.attr.aligned(N) 的整数字面量 N(缺参/非字面量返回 None)。
|
||||||
|
|
||||||
|
对应 TransPyV ClassGenBase._tattr_aligned_int(.vp:395-415)。
|
||||||
|
"""
|
||||||
|
if isinstance(node, ast.Call) and node.args:
|
||||||
|
arg0: ast.AST = node.args[0]
|
||||||
|
if (isinstance(arg0, ast.Constant) and isinstance(arg0.value, int)
|
||||||
|
and not isinstance(arg0.value, bool)):
|
||||||
|
return arg0.value
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _RaiseGlobalAttrError(self, Msg: str, Node: ast.AST, Gen: "Translator.LlvmGen", VarName: str) -> None:
|
||||||
|
"""fail-fast:全局 t.attr.* 属性非法(缺/空段名、非正整数字面量 aligned)。
|
||||||
|
|
||||||
|
定位串(文件:行:列)与报错出口对齐 HandlesBase._RaiseUnresolvedExpr
|
||||||
|
(HandlesBase.py:552-615)——错误信息含源码位置后 sys.exit(1)。
|
||||||
|
"""
|
||||||
|
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
|
||||||
|
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
|
||||||
|
col: int = getattr(Node, 'col_offset', -1)
|
||||||
|
src_file: str = getattr(Gen, '_current_source_file', '') or ''
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的行数
|
||||||
|
_line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and lineno > _line_offset:
|
||||||
|
lineno -= _line_offset
|
||||||
|
loc: str = src_file
|
||||||
|
if lineno > 0:
|
||||||
|
loc += f":{lineno}"
|
||||||
|
if col >= 0:
|
||||||
|
loc += f":{col}"
|
||||||
|
_vlog().compile_error(
|
||||||
|
f"模块级变量 '{VarName}' 的 {Msg}(位置 {loc})", src_file, lineno, '')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _ExtractGlobalAttrs(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> dict[str, Any]:
|
||||||
|
"""从模块级全局注解提取 t.attr.section/aligned/weak。
|
||||||
|
|
||||||
|
返回 {'section': str|None, 'align': int, 'weak': bool}。
|
||||||
|
非法值(空/缺段名、非正整数 aligned)→ fail-fast 终止(对齐 TransPyV
|
||||||
|
TranslatorGenMixin._extract_global_attr_section/aligned 的致命错误语义)。
|
||||||
|
"""
|
||||||
|
Attrs: dict[str, Any] = {'section': None, 'align': 0, 'weak': False}
|
||||||
|
Ann: ast.AST = Node.annotation
|
||||||
|
VarName: str = Node.target.id if isinstance(Node.target, ast.Name) else '<anonymous>'
|
||||||
|
sec_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'section')
|
||||||
|
if sec_node is not None:
|
||||||
|
sec_val: str | None = self._TAttrArgStr(sec_node)
|
||||||
|
if not sec_val:
|
||||||
|
self._RaiseGlobalAttrError(
|
||||||
|
"t.attr.section(...) 段名缺失或为空(空段名会生成非法的 section \"\")",
|
||||||
|
sec_node, Gen, VarName)
|
||||||
|
Attrs['section'] = sec_val
|
||||||
|
align_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'aligned')
|
||||||
|
if align_node is not None:
|
||||||
|
align_val: int | None = self._TAttrAlignInt(align_node)
|
||||||
|
if align_val is None or align_val <= 0:
|
||||||
|
self._RaiseGlobalAttrError(
|
||||||
|
"t.attr.aligned(N) 需要正整数字面量 N", align_node, Gen, VarName)
|
||||||
|
Attrs['align'] = align_val
|
||||||
|
if self._ScanGlobalTAttr(Ann, 'weak') is not None:
|
||||||
|
Attrs['weak'] = True
|
||||||
|
return Attrs
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _ApplyGlobalAttrs(GlobalVar: ir.GlobalVariable, Attrs: dict[str, Any]) -> None:
|
||||||
|
"""把提取出的 section/align/weak 施加到已创建的 LLVM 全局变量上。
|
||||||
|
|
||||||
|
llvmlite 口径(llvmlite/ir/values.py):
|
||||||
|
- section 写**裸**段名(序列化时自动加引号,:843-844);
|
||||||
|
- align 需 int(:846-847);GlobalVariable.align 默认 None(:807);
|
||||||
|
- weak 用 linkage='weak'(GlobalVariable 无 weak 属性,对照函数侧
|
||||||
|
HandlesFunctions.py:1772)。
|
||||||
|
"""
|
||||||
|
if not Attrs:
|
||||||
|
return
|
||||||
|
sec: str | None = Attrs.get('section')
|
||||||
|
if sec:
|
||||||
|
GlobalVar.section = sec
|
||||||
|
align_val: int = int(Attrs.get('align') or 0)
|
||||||
|
if align_val > 0:
|
||||||
|
GlobalVar.align = align_val
|
||||||
|
if Attrs.get('weak'):
|
||||||
|
GlobalVar.linkage = 'weak'
|
||||||
|
|
||||||
def _eval_global_const(self, value_node: ast.expr, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None:
|
def _eval_global_const(self, value_node: ast.expr, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None:
|
||||||
if isinstance(value_node, ast.Constant):
|
if isinstance(value_node, ast.Constant):
|
||||||
if isinstance(value_node.value, bool):
|
if isinstance(value_node.value, bool):
|
||||||
|
|||||||
@@ -303,9 +303,14 @@ class AsyncFunctionHandle(BaseHandle):
|
|||||||
if not any(isinstance(n, ast.Await) for n in ast.walk(stmt)):
|
if not any(isinstance(n, ast.Await) for n in ast.walk(stmt)):
|
||||||
continue # 无 await:委托 ForHandler,不需额外槽
|
continue # 无 await:委托 ForHandler,不需额外槽
|
||||||
if not self._IsRangeFor(stmt):
|
if not self._IsRangeFor(stmt):
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(stmt, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)"
|
f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)"
|
||||||
f"(line {getattr(stmt, 'lineno', 0)})"
|
f"(line {_lineno})"
|
||||||
)
|
)
|
||||||
slots.append(f'__f_idx_{for_counter}')
|
slots.append(f'__f_idx_{for_counter}')
|
||||||
slots.append(f'__f_len_{for_counter}')
|
slots.append(f'__f_len_{for_counter}')
|
||||||
@@ -551,9 +556,14 @@ class AsyncFunctionHandle(BaseHandle):
|
|||||||
if isinstance(stmt, ast.Expr):
|
if isinstance(stmt, ast.Expr):
|
||||||
self.Trans.ExprHandler.HandleExprLlvm(stmt.value)
|
self.Trans.ExprHandler.HandleExprLlvm(stmt.value)
|
||||||
return
|
return
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(stmt, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M2] async 函数体暂不支持语句: {type(stmt).__name__} "
|
f"[coro-M2] async 函数体暂不支持语句: {type(stmt).__name__} "
|
||||||
f"(line {getattr(stmt, 'lineno', 0)})"
|
f"(line {_lineno})"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@@ -640,9 +650,14 @@ class AsyncFunctionHandle(BaseHandle):
|
|||||||
return
|
return
|
||||||
async_name: str | None = self._GetAsyncCallName(call) if isinstance(call, ast.Call) else None
|
async_name: str | None = self._GetAsyncCallName(call) if isinstance(call, ast.Call) else None
|
||||||
if not async_name:
|
if not async_name:
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(await_node, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M3] await 目标必须是 async 函数调用或 coro_sleep(ms) "
|
f"[coro-M3] await 目标必须是 async 函数调用或 coro_sleep(ms) "
|
||||||
f"(line {getattr(await_node, 'lineno', 0)})"
|
f"(line {_lineno})"
|
||||||
)
|
)
|
||||||
self.Trans.ExprHandler.HandleExprLlvm(await_node) # → _HandleAwaitLlvm
|
self.Trans.ExprHandler.HandleExprLlvm(await_node) # → _HandleAwaitLlvm
|
||||||
|
|
||||||
@@ -1002,8 +1017,13 @@ class AsyncFunctionHandle(BaseHandle):
|
|||||||
|
|
||||||
def _HandleCoroIf(self, Node: ast.If, Gen: LlvmCodeGenerator) -> None:
|
def _HandleCoroIf(self, Node: ast.If, Gen: LlvmCodeGenerator) -> None:
|
||||||
if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)):
|
if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)):
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M2] if 条件中不支持 await (line {getattr(Node, 'lineno', 0)})"
|
f"[coro-M2] if 条件中不支持 await (line {_lineno})"
|
||||||
)
|
)
|
||||||
Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test)
|
Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test)
|
||||||
if not Cond:
|
if not Cond:
|
||||||
@@ -1034,8 +1054,13 @@ class AsyncFunctionHandle(BaseHandle):
|
|||||||
|
|
||||||
def _HandleCoroWhile(self, Node: ast.While, Gen: LlvmCodeGenerator) -> None:
|
def _HandleCoroWhile(self, Node: ast.While, Gen: LlvmCodeGenerator) -> None:
|
||||||
if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)):
|
if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)):
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M2] while 条件中不支持 await (line {getattr(Node, 'lineno', 0)})"
|
f"[coro-M2] while 条件中不支持 await (line {_lineno})"
|
||||||
)
|
)
|
||||||
CondBB: ir.Block = Gen.func.append_basic_block(name='coro_wcond')
|
CondBB: ir.Block = Gen.func.append_basic_block(name='coro_wcond')
|
||||||
BodyBB: ir.Block = Gen.func.append_basic_block(name='coro_wbody')
|
BodyBB: ir.Block = Gen.func.append_basic_block(name='coro_wbody')
|
||||||
@@ -1076,12 +1101,22 @@ class AsyncFunctionHandle(BaseHandle):
|
|||||||
def _HandleCoroTry(self, Node: ast.Try, Gen: LlvmCodeGenerator) -> None:
|
def _HandleCoroTry(self, Node: ast.Try, Gen: LlvmCodeGenerator) -> None:
|
||||||
"""协程内 try/except:捕获 raise 与 await 传播异常(M6:多 except 类型匹配;Exception/裸 except 捕获所有)。"""
|
"""协程内 try/except:捕获 raise 与 await 传播异常(M6:多 except 类型匹配;Exception/裸 except 捕获所有)。"""
|
||||||
if not Node.handlers:
|
if not Node.handlers:
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M6] 协程内 try 需要 except 子句 (line {getattr(Node, 'lineno', 0)})"
|
f"[coro-M6] 协程内 try 需要 except 子句 (line {_lineno})"
|
||||||
)
|
)
|
||||||
if Node.finalbody:
|
if Node.finalbody:
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
_line_offset = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M6] 协程内暂不支持 try/finally (line {getattr(Node, 'lineno', 0)})"
|
f"[coro-M6] 协程内暂不支持 try/finally (line {_lineno})"
|
||||||
)
|
)
|
||||||
except_bb: ir.Block = Gen.func.append_basic_block('coro_try_except')
|
except_bb: ir.Block = Gen.func.append_basic_block('coro_try_except')
|
||||||
merge_bb: ir.Block = Gen.func.append_basic_block('coro_try_merge')
|
merge_bb: ir.Block = Gen.func.append_basic_block('coro_try_merge')
|
||||||
@@ -1159,8 +1194,13 @@ class AsyncFunctionHandle(BaseHandle):
|
|||||||
"""协程内 try/finally(无 except):正常/异常路径都执行 fbody;
|
"""协程内 try/finally(无 except):正常/异常路径都执行 fbody;
|
||||||
异常时 finally 后重新传播(done=1 + br exit);finally 内暂不支持 await。"""
|
异常时 finally 后重新传播(done=1 + br exit);finally 内暂不支持 await。"""
|
||||||
if any(isinstance(n, ast.Await) for s in Node.finalbody for n in ast.walk(s)):
|
if any(isinstance(n, ast.Await) for s in Node.finalbody for n in ast.walk(s)):
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M7] finally 内暂不支持 await (line {getattr(Node, 'lineno', 0)})"
|
f"[coro-M7] finally 内暂不支持 await (line {_lineno})"
|
||||||
)
|
)
|
||||||
fin_bb: ir.Block = Gen.func.append_basic_block('coro_fin')
|
fin_bb: ir.Block = Gen.func.append_basic_block('coro_fin')
|
||||||
fin_exc_bb: ir.Block = Gen.func.append_basic_block('coro_fin_exc')
|
fin_exc_bb: ir.Block = Gen.func.append_basic_block('coro_fin_exc')
|
||||||
@@ -1201,15 +1241,25 @@ class AsyncFunctionHandle(BaseHandle):
|
|||||||
def _HandleCoroFor(self, Node: ast.For, Gen: LlvmCodeGenerator) -> None:
|
def _HandleCoroFor(self, Node: ast.For, Gen: LlvmCodeGenerator) -> None:
|
||||||
if any(isinstance(n, ast.Await) for n in ast.walk(Node)):
|
if any(isinstance(n, ast.Await) for n in ast.walk(Node)):
|
||||||
if not self._IsRangeFor(Node):
|
if not self._IsRangeFor(Node):
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)"
|
f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)"
|
||||||
f"(line {getattr(Node, 'lineno', 0)})"
|
f"(line {_lineno})"
|
||||||
)
|
)
|
||||||
self._HandleCoroForRange(Node, Gen)
|
self._HandleCoroForRange(Node, Gen)
|
||||||
return
|
return
|
||||||
if any(isinstance(n, ast.Return) for st in Node.body for n in ast.walk(st)):
|
if any(isinstance(n, ast.Return) for st in Node.body for n in ast.walk(st)):
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[coro-M2] For 循环体内暂不支持 return(M3 实现)(line {getattr(Node, 'lineno', 0)})"
|
f"[coro-M2] For 循环体内暂不支持 return(M3 实现)(line {_lineno})"
|
||||||
)
|
)
|
||||||
# 无 await/return:委托现有 ForHandler(迭代索引等 alloca 不跨挂起,安全)
|
# 无 await/return:委托现有 ForHandler(迭代索引等 alloca 不跨挂起,安全)
|
||||||
self.Trans.ForHandler._HandleForLlvm(Node)
|
self.Trans.ForHandler._HandleForLlvm(Node)
|
||||||
|
|||||||
@@ -65,9 +65,8 @@ class AugAssignHandle(BaseHandle):
|
|||||||
src_info: str = Gen._get_node_info()
|
src_info: str = Gen._get_node_info()
|
||||||
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
|
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if VarName in Gen.var_const_flags:
|
if Gen._IsVarConst(VarName):
|
||||||
src_info: str = Gen._get_node_info()
|
_vlog().error(Gen._ConstAssignMessage("增量赋值", VarName))
|
||||||
_vlog().error(f"不能对 const 变量 '{VarName}' 增量赋值{src_info}")
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
ClassName: str | None = Gen.var_struct_class.get(VarName)
|
ClassName: str | None = Gen.var_struct_class.get(VarName)
|
||||||
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
|
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
|
||||||
@@ -118,7 +117,7 @@ class AugAssignHandle(BaseHandle):
|
|||||||
GVar = Gen.module.globals[VarName]
|
GVar = Gen.module.globals[VarName]
|
||||||
Gen.variables[VarName] = GVar
|
Gen.variables[VarName] = GVar
|
||||||
OldVal: ir.Value = Gen._load(GVar, name=VarName)
|
OldVal: ir.Value = Gen._load(GVar, name=VarName)
|
||||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
|
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||||
Gen._store(NewVal, GVar)
|
Gen._store(NewVal, GVar)
|
||||||
return
|
return
|
||||||
if VarName in Gen._reg_values:
|
if VarName in Gen._reg_values:
|
||||||
@@ -156,7 +155,7 @@ class AugAssignHandle(BaseHandle):
|
|||||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
|
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
|
||||||
elif OldVal.type.width > Value.type.width:
|
elif OldVal.type.width > Value.type.width:
|
||||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
|
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
|
||||||
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
|
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||||
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
|
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
|
||||||
if NewVal.type.width > orig_type.width:
|
if NewVal.type.width > orig_type.width:
|
||||||
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
|
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
|
||||||
@@ -179,7 +178,7 @@ class AugAssignHandle(BaseHandle):
|
|||||||
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
|
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
|
||||||
Gen._store(NewVal, store_ptr)
|
Gen._store(NewVal, store_ptr)
|
||||||
return
|
return
|
||||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
|
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||||
if isinstance(store_ptr.type, ir.PointerType):
|
if isinstance(store_ptr.type, ir.PointerType):
|
||||||
TargetType: ir.Type = store_ptr.type.pointee
|
TargetType: ir.Type = store_ptr.type.pointee
|
||||||
if NewVal.type != TargetType:
|
if NewVal.type != TargetType:
|
||||||
@@ -191,7 +190,7 @@ class AugAssignHandle(BaseHandle):
|
|||||||
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
|
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
|
||||||
Gen.variables[VarName] = var
|
Gen.variables[VarName] = var
|
||||||
OldVal: ir.Value = Gen._load(var, name=VarName)
|
OldVal: ir.Value = Gen._load(var, name=VarName)
|
||||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
|
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||||
Gen._store(NewVal, var)
|
Gen._store(NewVal, var)
|
||||||
elif isinstance(Node.target, ast.Attribute):
|
elif isinstance(Node.target, ast.Attribute):
|
||||||
AttrName: str = Node.target.attr
|
AttrName: str = Node.target.attr
|
||||||
@@ -210,7 +209,7 @@ class AugAssignHandle(BaseHandle):
|
|||||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
|
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
|
||||||
elif OldVal.type.width > Value.type.width:
|
elif OldVal.type.width > Value.type.width:
|
||||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
|
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
|
||||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
|
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||||
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
|
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
|
||||||
elif isinstance(Node.target, ast.Subscript):
|
elif isinstance(Node.target, ast.Subscript):
|
||||||
SubTarget: ast.Subscript = Node.target
|
SubTarget: ast.Subscript = Node.target
|
||||||
@@ -236,7 +235,7 @@ class AugAssignHandle(BaseHandle):
|
|||||||
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
|
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
|
||||||
Gen._store(NewVal, ElemPtr)
|
Gen._store(NewVal, ElemPtr)
|
||||||
return
|
return
|
||||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
|
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||||
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
|
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
|
||||||
if NewVal.type.width > ElemPtr.type.pointee.width:
|
if NewVal.type.width > ElemPtr.type.pointee.width:
|
||||||
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
|
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
|
||||||
|
|||||||
+122
-20
@@ -13,7 +13,8 @@ from lib.constants import config as _config
|
|||||||
from lib.includes import t
|
from lib.includes import t
|
||||||
from lib.includes.t import CTypeRegistry
|
from lib.includes.t import CTypeRegistry
|
||||||
from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES
|
from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES
|
||||||
from lib.core.SymbolUtils import IsPtrSubscriptNode, IsListAnnotation
|
from lib.core.SymbolUtils import (
|
||||||
|
IsPtrSubscriptNode, IsListAnnotation, FixedArrayKeywordAnnotationError)
|
||||||
from lib.core.VLogger import get_logger as _vlog
|
from lib.core.VLogger import get_logger as _vlog
|
||||||
|
|
||||||
|
|
||||||
@@ -523,12 +524,13 @@ class BaseHandle:
|
|||||||
pointee: _ir.Type = val.type.pointee
|
pointee: _ir.Type = val.type.pointee
|
||||||
return isinstance(pointee, _ir.IntType) and pointee.width == 8
|
return isinstance(pointee, _ir.IntType) and pointee.width == 8
|
||||||
|
|
||||||
def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None) -> _ir.Value | None:
|
def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None,
|
||||||
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
|
ConstWidthHint: int | None = None) -> _ir.Value | None:
|
||||||
|
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _ExprText(Node: ast.AST) -> str:
|
def _ExprText(Node: ast.AST) -> str:
|
||||||
"""把属性链/名字节点渲染成源码文本(如 func.Blocks.Head),供错误信息使用"""
|
"""把表达式节点渲染成源码文本(如 func.Blocks.Head、list[X*] * uint64(v)),供错误信息使用"""
|
||||||
if isinstance(Node, ast.Attribute):
|
if isinstance(Node, ast.Attribute):
|
||||||
parts: list[str] = []
|
parts: list[str] = []
|
||||||
cur: ast.AST = Node
|
cur: ast.AST = Node
|
||||||
@@ -541,32 +543,97 @@ class BaseHandle:
|
|||||||
return '.'.join(reversed(parts))
|
return '.'.join(reversed(parts))
|
||||||
if isinstance(Node, ast.Name):
|
if isinstance(Node, ast.Name):
|
||||||
return Node.id
|
return Node.id
|
||||||
|
try:
|
||||||
|
rendered: str = ast.unparse(Node)
|
||||||
|
if rendered:
|
||||||
|
return rendered
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return getattr(Node, 'attr', '') or '<expr>'
|
return getattr(Node, 'attr', '') or '<expr>'
|
||||||
|
|
||||||
def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any) -> None:
|
def _FailFastFixedArrayKeyword(self, Node: ast.AST, Msg: str) -> None:
|
||||||
"""fail-fast:属性/成员访问表达式在编译期求值失败(返回 None)时终止。
|
"""内建定长数组构造子 array 误用的 fail-fast 出口。
|
||||||
|
|
||||||
属性链(含两级自动解引用链,如 blk = func.Blocks.Head)若解析不出
|
报错含:文件:行:列 + 源码片段 + 期望形态(array[E, N])+ 正确示例
|
||||||
目标类型,_handle_attr_nested 会返回 None;赋值调用方若静默跳过
|
(消息文本由 SymbolUtils.FixedArrayKeywordAnnotationError 给出)。
|
||||||
store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机崩溃)。
|
|
||||||
此处用 VLogger 输出 文件名:行:列 + 表达式源码后退出(严格模式语义)。
|
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
Gen: Any = getattr(self.Trans, 'LlvmGen', None)
|
||||||
|
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
|
||||||
|
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
|
||||||
|
col: int = getattr(Node, 'col_offset', -1)
|
||||||
|
src_file: str = getattr(Gen, '_current_source_file', '') or ''
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and lineno > _line_offset:
|
||||||
|
lineno -= _line_offset
|
||||||
|
code_line: str = ''
|
||||||
|
if lineno > 0 and Gen is not None and hasattr(Gen, '_get_source_line'):
|
||||||
|
try:
|
||||||
|
code_line = (Gen._get_source_line(lineno) or '').strip()
|
||||||
|
except Exception:
|
||||||
|
code_line = ''
|
||||||
|
loc: str = src_file
|
||||||
|
if lineno > 0:
|
||||||
|
loc += f":{lineno}"
|
||||||
|
if col >= 0:
|
||||||
|
loc += f":{col}"
|
||||||
|
full: str = f"{Msg}。位置 {loc}" + (f",源码: {code_line}" if code_line else '')
|
||||||
|
_vlog().compile_error(full, src_file, lineno, code_line)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _CheckFixedArrayKeyword(self, Annotation: ast.AST) -> None:
|
||||||
|
"""注解入口统一校验内建定长数组构造子 array(误用即 fail-fast)。
|
||||||
|
|
||||||
|
只校验带位置的注解入口(本类的 ResolveAnnotationType / CollectAnnAssignMember
|
||||||
|
+ HandlesAnnAssign 局部 / HandlesAssign 全局显式调用),**不**在通用表达式
|
||||||
|
解析路径上调用——以保证 array 在非类型位置仍是普通标识符(软关键字)。
|
||||||
|
"""
|
||||||
|
if Annotation is None:
|
||||||
|
return
|
||||||
|
Msg: str | None = FixedArrayKeywordAnnotationError(Annotation, self.Trans.SymbolTable)
|
||||||
|
if Msg:
|
||||||
|
self._FailFastFixedArrayKeyword(Annotation, Msg)
|
||||||
|
|
||||||
|
def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any, AnnNode: ast.AST | None = None,
|
||||||
|
VarName: str | None = None) -> None:
|
||||||
|
"""fail-fast:带值表达式在编译期求值失败(返回 None 且无值可存)时终止。
|
||||||
|
|
||||||
|
属性链(含两级自动解引用链,如 blk = func.Blocks.Head)解析不出目标
|
||||||
|
类型时 _handle_attr_nested 会返回 None;类型下标被当乘法的畸形写法
|
||||||
|
(如 list[X*]*(...))会让整条 RHS 不产任何指令而求值 None。赋值调用方
|
||||||
|
若静默跳过 store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机
|
||||||
|
崩溃)。此处用 VLogger 输出 目标变量 + 注解文本 + 表达式源码与形态 +
|
||||||
|
文件名:行:列 后退出(严格模式语义)。
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
ann_text: str = ''
|
||||||
|
if AnnNode is not None:
|
||||||
|
try:
|
||||||
|
ann_text = ast.unparse(AnnNode)
|
||||||
|
except Exception:
|
||||||
|
ann_text = ''
|
||||||
|
# RHS 形态 = AST 节点类名(Name/Subscript/BinOp/Call/Attribute/…),
|
||||||
|
# 便于报错可直接按形态分类,不必再从表达式文本反推。
|
||||||
|
rhs_kind: str = type(Node).__name__
|
||||||
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
|
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
|
||||||
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
|
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
|
||||||
col: int = getattr(Node, 'col_offset', -1)
|
col: int = getattr(Node, 'col_offset', -1)
|
||||||
src_file: str = getattr(Gen, '_current_source_file', '') or ''
|
src_file: str = getattr(Gen, '_current_source_file', '') or ''
|
||||||
expr_text: str = self._ExprText(Node)
|
expr_text: str = self._ExprText(Node)
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
|
||||||
|
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator)。
|
||||||
|
_line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and lineno > _line_offset:
|
||||||
|
lineno -= _line_offset
|
||||||
code_line: str = ''
|
code_line: str = ''
|
||||||
if lineno > 0 and src_file:
|
if lineno > 0 and src_file:
|
||||||
# 行号基准修正(vasts AST 行号 vs 磁盘原文件行号偏差):
|
# 行号基准修正(vast AST 行号 vs 磁盘原文件行号偏差):
|
||||||
# Phase2Translator._inject_auto_imports 会在解析前向源码头部注入
|
# Phase2Translator._inject_auto_imports 会在解析前向源码头部注入
|
||||||
# 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件
|
# 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件
|
||||||
# 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、
|
# 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、
|
||||||
# FuncGenMixin.vp 真实 514 报 515)。而 _get_source_line 基于
|
# FuncGenMixin.vp 真实 514 报 515)。上面已按 _line_offset 精确还原;
|
||||||
# _current_source_lines(原文件),导致报错行号偏大且"代码行"
|
# 此处再以表达式文本为锚(从原始行号向上至多 8 行)回溯校正
|
||||||
# 取错行。修正:以表达式文本为锚,从原始行号向上(至多 8 行)
|
# 属性链(其 expr_text 逐字出现在磁盘行中)可能残留的偏差。
|
||||||
# 回溯取"最近且包含该表达式文本"的源码行作为真实行号。
|
|
||||||
if expr_text and '.' in expr_text and hasattr(Gen, '_get_source_line'):
|
if expr_text and '.' in expr_text and hasattr(Gen, '_get_source_line'):
|
||||||
_src_lines: Any = getattr(Gen, '_current_source_lines', None)
|
_src_lines: Any = getattr(Gen, '_current_source_lines', None)
|
||||||
if isinstance(_src_lines, list) and _src_lines:
|
if isinstance(_src_lines, list) and _src_lines:
|
||||||
@@ -583,8 +650,11 @@ class BaseHandle:
|
|||||||
loc += f":{lineno}"
|
loc += f":{lineno}"
|
||||||
if col >= 0:
|
if col >= 0:
|
||||||
loc += f":{col}"
|
loc += f":{col}"
|
||||||
msg: str = (f"{What}表达式 '{expr_text}' 编译期求值为空(属性访问未能解析出"
|
msg: str = (f"{What}表达式 '{expr_text}'({rhs_kind} 形态)编译期求值为空("
|
||||||
f"目标类型/成员),赋值将被跳过、变量保持未初始化。位置 {loc}"
|
f"表达式未解析出目标、无值可存;且其求值未以返回 void 的调用收尾),"
|
||||||
|
f"赋值将被跳过、变量保持未初始化"
|
||||||
|
f"{(',目标变量 ' + repr(VarName)) if VarName else ''}"
|
||||||
|
f"{(',注解 ' + ann_text) if ann_text else ''}。位置 {loc}"
|
||||||
f"{(',源码: ' + code_line) if code_line else ''}")
|
f"{(',源码: ' + code_line) if code_line else ''}")
|
||||||
_vlog().compile_error(msg, src_file, lineno, code_line)
|
_vlog().compile_error(msg, src_file, lineno, code_line)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -767,6 +837,9 @@ class BaseHandle:
|
|||||||
CTypeInfo.FromNode 把容器折叠成 i8*,字段类型与 declare/stub 端
|
CTypeInfo.FromNode 把容器折叠成 i8*,字段类型与 declare/stub 端
|
||||||
的特化名指针不一致(同一结构体在定义端/声明端布局描述不同)。
|
的特化名指针不一致(同一结构体在定义端/声明端布局描述不同)。
|
||||||
"""
|
"""
|
||||||
|
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast):
|
||||||
|
# 本方法覆盖函数参数/返回注解与类字段(CollectAnnAssignMember 亦经此)。
|
||||||
|
self._CheckFixedArrayKeyword(annotation)
|
||||||
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
|
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
|
||||||
merger: Any = getattr(self.Trans, 'TypeMergeHandler', None)
|
merger: Any = getattr(self.Trans, 'TypeMergeHandler', None)
|
||||||
if merger:
|
if merger:
|
||||||
@@ -943,6 +1016,15 @@ class BaseHandle:
|
|||||||
if isinstance(MemberType, _ir.VoidType):
|
if isinstance(MemberType, _ir.VoidType):
|
||||||
MemberType = _ir.PointerType(_ir.IntType(8))
|
MemberType = _ir.PointerType(_ir.IntType(8))
|
||||||
return VarName, MemberType, TypeInfo
|
return VarName, MemberType, TypeInfo
|
||||||
|
except RecursionError:
|
||||||
|
# RecursionError 不是"可忽略异常":它是类型解析/声明发射链**成环**的
|
||||||
|
# 真缺陷信号。此前被下方 `except Exception` 吞成一行
|
||||||
|
# `WARN: HandlesBase: 忽略异常 maximum recursion depth exceeded` 并静默
|
||||||
|
# 退化为 i32 ⇒ 真正的环(如跨模块类声明发射重入
|
||||||
|
# _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub ↔ _TryLoadClassMembersFromPyi)
|
||||||
|
# 被掩盖,下游只看到类型解析静默退化。此处 fail-fast 上抛(保留原始栈),
|
||||||
|
# 让环路根因暴露在错误栈顶,不再被 WARN 掩埋。
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_vlog().warning(f"HandlesBase: 忽略异常 {e}", exc_info=e)
|
_vlog().warning(f"HandlesBase: 忽略异常 {e}", exc_info=e)
|
||||||
return VarName, _ir.IntType(32), None
|
return VarName, _ir.IntType(32), None
|
||||||
@@ -995,15 +1077,35 @@ class BaseHandle:
|
|||||||
ret_type = inferred
|
ret_type = inferred
|
||||||
else:
|
else:
|
||||||
ret_type = Gen._CType2LLVM('i32', False)
|
ret_type = Gen._CType2LLVM('i32', False)
|
||||||
param_type_infos = [pt for _, pt in (sym_info.FuncPtrParams or [])]
|
param_type_infos = list(sym_info.FuncPtrParams or [])
|
||||||
if isinstance(ret_type, _ir.VoidType):
|
if isinstance(ret_type, _ir.VoidType):
|
||||||
ret_type = _ir.IntType(32)
|
ret_type = _ir.IntType(32)
|
||||||
llvm_param_types: list[Any] = []
|
llvm_param_types: list[Any] = []
|
||||||
for pt in param_type_infos:
|
for ParamIdx, (ParamName, pt) in enumerate(param_type_infos):
|
||||||
if isinstance(pt, CTypeInfo) and pt.BaseType:
|
if isinstance(pt, CTypeInfo) and pt.BaseType:
|
||||||
lp: Any = pt.ToLLVM(Gen)
|
lp: Any = pt.ToLLVM(Gen)
|
||||||
|
elif isinstance(pt, str):
|
||||||
|
lp = Gen._CType2LLVM(pt, '*' in pt)
|
||||||
else:
|
else:
|
||||||
lp = Gen._CType2LLVM(pt if isinstance(pt, str) else 'i32', '*' in pt if isinstance(pt, str) else False)
|
# fast-fail:形参类型未解析出 BaseType 时不再硬编码 'i32' 造形参。
|
||||||
|
# 旧回退会把"类型解析失败"伪装成 llvmlite 的
|
||||||
|
# Type of #{N} arg mismatch: {expected} != {actual}
|
||||||
|
# (形参被静默写成 i32 ⇒ 报错方向与真因相反,且不含符号/位置信息,
|
||||||
|
# 如 "Type of #2 arg mismatch: i32 != %"<sha1>.SymbolTable"*"),
|
||||||
|
# 这里直接抛出带上下文的编译错误暴露真实缺失。
|
||||||
|
if isinstance(pt, CTypeInfo):
|
||||||
|
_PtDiag: str = (
|
||||||
|
f"CTypeInfo(BaseType=None, Name={pt.Name!r}, PtrCount={pt.PtrCount},"
|
||||||
|
f" IsTypedef={pt.IsTypedef}, IsFuncPtr={pt.IsFuncPtr}, IsState={pt.IsState},"
|
||||||
|
f" OriginalType={pt.OriginalType!r})")
|
||||||
|
else:
|
||||||
|
_PtDiag = f"{type(pt).__name__}({pt!r})"
|
||||||
|
raise TypeError(
|
||||||
|
f"[fast-fail] 函数 '{sym_info.Name}' 第 {ParamIdx + 1} 个形参 '{ParamName}' 的"
|
||||||
|
f"类型未解析出 BaseType,无法构建 LLVM 形参类型({_PtDiag});"
|
||||||
|
f"定义位置={getattr(sym_info, 'file', None)!r}:{sym_info.Lineno},"
|
||||||
|
f" IsVariadic={is_variadic}, mangled_name={mangled_name!r}"
|
||||||
|
)
|
||||||
if isinstance(lp, _ir.VoidType):
|
if isinstance(lp, _ir.VoidType):
|
||||||
lp = _ir.IntType(8).as_pointer()
|
lp = _ir.IntType(8).as_pointer()
|
||||||
llvm_param_types.append(lp)
|
llvm_param_types.append(lp)
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ class BodyHandle(BaseHandle):
|
|||||||
self.Trans.DeleteHandler._HandleDeleteLlvm(Node)
|
self.Trans.DeleteHandler._HandleDeleteLlvm(Node)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
lineno: int = getattr(Node, 'lineno', 0)
|
lineno: int = getattr(Node, 'lineno', 0)
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
|
||||||
|
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator);
|
||||||
|
# 报文行号与源码行必须同用还原后的 lineno,二者一致(否则会显示下一行源码)。
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and lineno > _line_offset:
|
||||||
|
lineno -= _line_offset
|
||||||
source_line: str | None = Gen._get_source_line(lineno)
|
source_line: str | None = Gen._get_source_line(lineno)
|
||||||
src_file: str = Gen._current_source_file or ''
|
src_file: str = Gen._current_source_file or ''
|
||||||
src_path: str = src_file if src_file else 'unknown'
|
src_path: str = src_file if src_file else 'unknown'
|
||||||
@@ -73,5 +79,6 @@ class BodyHandle(BaseHandle):
|
|||||||
if Gen.builder and not Gen.builder.block.is_terminated:
|
if Gen.builder and not Gen.builder.block.is_terminated:
|
||||||
Gen._EmitTempFrees()
|
Gen._EmitTempFrees()
|
||||||
|
|
||||||
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None) -> ir.Value | None:
|
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None,
|
||||||
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
|
ConstWidthHint: int | None = None) -> ir.Value | None:
|
||||||
|
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
|
||||||
@@ -9,12 +9,22 @@ import llvmlite.ir as ir
|
|||||||
from lib.core.Exportable import EnumMember as ExportEnumMember
|
from lib.core.Exportable import EnumMember as ExportEnumMember
|
||||||
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
|
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
|
||||||
from lib.core.SymbolUtils import (
|
from lib.core.SymbolUtils import (
|
||||||
IsTModule, FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry)
|
IsTModule, FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry,
|
||||||
|
EnumClassIntWidth,
|
||||||
|
EnumBodyMemberNames, EnumMemberCountMismatchError,
|
||||||
|
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
|
||||||
from lib.core.VLogger import get_logger as _vlog
|
from lib.core.VLogger import get_logger as _vlog
|
||||||
from lib.constants.config import mode as _config_mode
|
from lib.constants.config import mode as _config_mode
|
||||||
from lib.includes import t
|
from lib.includes import t
|
||||||
from lib.includes.t import CTypeRegistry
|
from lib.includes.t import CTypeRegistry
|
||||||
|
|
||||||
|
# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CNeedPtr/CAutoPtr,后两者继承
|
||||||
|
# CPtr)、内建 rawptr(= t.CPtr)、str/bytes(= i8*)。枚举值是整数常量,
|
||||||
|
# 指针成员语义不成立,命中即明确报错(不静默当 int)。
|
||||||
|
_ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({
|
||||||
|
'CPtr', 'CNeedPtr', 'CAutoPtr', 'rawptr', 'str', 'bytes',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class ClassHandle(BaseHandle):
|
class ClassHandle(BaseHandle):
|
||||||
def _is_exception_class(self, Node: ast.ClassDef) -> bool:
|
def _is_exception_class(self, Node: ast.ClassDef) -> bool:
|
||||||
@@ -438,11 +448,15 @@ class ClassHandle(BaseHandle):
|
|||||||
return 'struct'
|
return 'struct'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool]:
|
def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool, bool]:
|
||||||
"""解析类的 Object/CVTable 标志和父类信息
|
"""解析类的 Object/CVTable 标志和父类信息
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple: (IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable)
|
tuple: (IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable,
|
||||||
|
VtableByClassLevel)
|
||||||
|
VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态(= 类级
|
||||||
|
CVTable / 继承链判定),供 vtable 槽筛选(SymbolUtils.IsVtableSlotMethod)
|
||||||
|
使用——与 TPV _should_method_be_virtual 第 3/4 条的判据一致。
|
||||||
"""
|
"""
|
||||||
ClassName: str = Node.name
|
ClassName: str = Node.name
|
||||||
IsCpythonObject: bool = False
|
IsCpythonObject: bool = False
|
||||||
@@ -536,6 +550,15 @@ class ClassHandle(BaseHandle):
|
|||||||
or ParentClassName in Gen.structs)
|
or ParentClassName in Gen.structs)
|
||||||
if parent_has_vtable or not parent_known:
|
if parent_has_vtable or not parent_known:
|
||||||
IsCVTable = True
|
IsCVTable = True
|
||||||
|
# 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable:
|
||||||
|
# 与 TPV ClassGenBase._detect_vtable_status 的三条 `_has_any_func_cvtable`
|
||||||
|
# 分支逐条对齐(类级 NoVTable / 父类 NoVTable / 无真实父类)。
|
||||||
|
# 注意"父类已知且无 vtable" **不**触发(TPV 同分支返回 0)——
|
||||||
|
# 父类按无 vptr 布局而子类插 vptr 会让继承方法字段错位。
|
||||||
|
VtableByClassLevel: bool = IsCVTable
|
||||||
|
if (not VtableByClassLevel and ClassHasFuncLevelCVTable(Node)
|
||||||
|
and (IsNoVTable or ParentIsNoVTable or not HasParentClass)):
|
||||||
|
IsCVTable = True
|
||||||
if IsCVTable:
|
if IsCVTable:
|
||||||
Gen.class_vtable.add(ClassName)
|
Gen.class_vtable.add(ClassName)
|
||||||
# 沿继承链向上传播,将所有祖先类加入 vtable
|
# 沿继承链向上传播,将所有祖先类加入 vtable
|
||||||
@@ -557,7 +580,7 @@ class ClassHandle(BaseHandle):
|
|||||||
else:
|
else:
|
||||||
# 类图中没有,尝试从 class_parent 查询
|
# 类图中没有,尝试从 class_parent 查询
|
||||||
ancestor = Gen.class_parent.get(ancestor)
|
ancestor = Gen.class_parent.get(ancestor)
|
||||||
return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable
|
return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel
|
||||||
|
|
||||||
def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None:
|
def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None:
|
||||||
"""注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。
|
"""注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。
|
||||||
@@ -651,7 +674,8 @@ class ClassHandle(BaseHandle):
|
|||||||
IsCVTable: bool
|
IsCVTable: bool
|
||||||
ParentClassName: str | None
|
ParentClassName: str | None
|
||||||
IsNoVTable: bool
|
IsNoVTable: bool
|
||||||
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable = self._resolve_class_flags(Node, Gen)
|
VtableByClassLevel: bool
|
||||||
|
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel = self._resolve_class_flags(Node, Gen)
|
||||||
# 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body
|
# 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body
|
||||||
Gen._classes_in_progress.add(ClassName)
|
Gen._classes_in_progress.add(ClassName)
|
||||||
# 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class)
|
# 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class)
|
||||||
@@ -686,6 +710,8 @@ class ClassHandle(BaseHandle):
|
|||||||
if _ck in _ih._struct_Load_cache:
|
if _ck in _ih._struct_Load_cache:
|
||||||
del _ih._struct_Load_cache[_ck]
|
del _ih._struct_Load_cache[_ck]
|
||||||
Gen.class_members[ClassName] = []
|
Gen.class_members[ClassName] = []
|
||||||
|
# 本地定义是权威重建点:先撤销"已展平"登记,重建完成(且父类齐全)后再登记。
|
||||||
|
Gen._members_flattened.discard(ClassName)
|
||||||
Gen.class_member_defaults[ClassName] = {}
|
Gen.class_member_defaults[ClassName] = {}
|
||||||
Gen.class_member_signeds[ClassName] = {}
|
Gen.class_member_signeds[ClassName] = {}
|
||||||
Gen.class_member_bitfields[ClassName] = {}
|
Gen.class_member_bitfields[ClassName] = {}
|
||||||
@@ -693,6 +719,9 @@ class ClassHandle(BaseHandle):
|
|||||||
Gen.class_member_bitoffsets[ClassName] = {}
|
Gen.class_member_bitoffsets[ClassName] = {}
|
||||||
ParentClass: str | None = None
|
ParentClass: str | None = None
|
||||||
ParentClasses: list[str] = []
|
ParentClasses: list[str] = []
|
||||||
|
# 本类展平是否完整(父类若在此刻尚未就绪会被跳过 ⇒ 不登记已展平,
|
||||||
|
# 留待查询侧 HandlesImports.EnsureClassMembersFlattened 补齐,顺序无关)。
|
||||||
|
_local_flat_complete: bool = True
|
||||||
if Node.bases:
|
if Node.bases:
|
||||||
class_graph: dict = Gen._class_graph
|
class_graph: dict = Gen._class_graph
|
||||||
for base in Node.bases:
|
for base in Node.bases:
|
||||||
@@ -784,6 +813,9 @@ class ClassHandle(BaseHandle):
|
|||||||
_flat: list[tuple] = []
|
_flat: list[tuple] = []
|
||||||
for ParentClass in ParentClasses:
|
for ParentClass in ParentClasses:
|
||||||
if ParentClass not in class_graph and ParentClass not in Gen.class_members:
|
if ParentClass not in class_graph and ParentClass not in Gen.class_members:
|
||||||
|
# 父类尚未就绪(跨模块父类在此刻未加载 / 特化表未生成)⇒
|
||||||
|
# 本类展平不完整,不登记 _members_flattened。
|
||||||
|
_local_flat_complete = False
|
||||||
continue
|
continue
|
||||||
_FlattenInherited(ParentClass, _seen_cls, _flat, Gen)
|
_FlattenInherited(ParentClass, _seen_cls, _flat, Gen)
|
||||||
if _flat:
|
if _flat:
|
||||||
@@ -871,6 +903,10 @@ class ClassHandle(BaseHandle):
|
|||||||
if parsed:
|
if parsed:
|
||||||
VarName, MemberType, TypeInfo = parsed
|
VarName, MemberType, TypeInfo = parsed
|
||||||
Gen.class_members[ClassName].append((VarName, MemberType))
|
Gen.class_members[ClassName].append((VarName, MemberType))
|
||||||
|
# const / volatile 类字段登记(统一登记入口,见 BaseGen)。
|
||||||
|
# 键 `类名.字段名` + 裸字段名;与 TransPyV 侧 ClassGenDef 的
|
||||||
|
# FieldEntry.IsConst/IsVolatile/PointeeVolatile 同口径。
|
||||||
|
Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation)
|
||||||
if TypeInfo:
|
if TypeInfo:
|
||||||
if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8:
|
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):
|
if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr):
|
||||||
@@ -1025,14 +1061,30 @@ class ClassHandle(BaseHandle):
|
|||||||
# 构造函数(__new__/__init__)不放入 vtable,避免子类与基类
|
# 构造函数(__new__/__init__)不放入 vtable,避免子类与基类
|
||||||
# vtable 大小不一致导致虚分派索引偏移。__before_init__ 由 TPC
|
# vtable 大小不一致导致虚分派索引偏移。__before_init__ 由 TPC
|
||||||
# 自动生成,不会出现在 FunctionDef 中,此处一并防御。
|
# 自动生成,不会出现在 FunctionDef 中,此处一并防御。
|
||||||
|
#
|
||||||
|
# 方法级修饰符(`novirtual def m(...)` ⇒ @t.NoVTable /
|
||||||
|
# `virtual def m(...)` ⇒ @t.CVTable):是否占槽按
|
||||||
|
# SymbolUtils.IsVtableSlotMethod 判定——与 TPV
|
||||||
|
# ClassGenBase._should_method_be_virtual 同判据(**唯一规则源**),
|
||||||
|
# 故 TPC/TPV 的虚表槽位顺序与数量逐槽一致(含"类级 NoVTable +
|
||||||
|
# 方法级 CVTable ⇒ 仍生成虚表且只有该方法进槽"这一组合形态)。
|
||||||
|
# 仅在本类确有 vtable(IsCVTable)时筛选:无 vtable 的类不进
|
||||||
|
# Vtables,class_methods 仍保留全部方法供静态解析/继承包装使用。
|
||||||
if MethodName not in ('__new__', '__init__', '__before_init__'):
|
if MethodName not in ('__new__', '__init__', '__before_init__'):
|
||||||
if FullMethodName not in Gen.class_methods[ClassName]:
|
_IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(item, VtableByClassLevel)
|
||||||
|
if _IsSlot and FullMethodName not in Gen.class_methods[ClassName]:
|
||||||
Gen.class_methods[ClassName].append(FullMethodName)
|
Gen.class_methods[ClassName].append(FullMethodName)
|
||||||
Gen.register_method(ClassName, FullMethodName)
|
Gen.register_method(ClassName, FullMethodName, _IsSlot)
|
||||||
|
else:
|
||||||
|
Gen.register_method(ClassName, FullMethodName)
|
||||||
if IsCVTable:
|
if IsCVTable:
|
||||||
Gen.class_vtable.add(ClassName)
|
Gen.class_vtable.add(ClassName)
|
||||||
Gen._generate_structs()
|
Gen._generate_structs()
|
||||||
Gen._create_Vtable_globals()
|
Gen._create_Vtable_globals()
|
||||||
|
if _local_flat_complete:
|
||||||
|
# 登记"该表已按父类展平":供 _TryLoadClassMembersFromPyi 的 guard 与
|
||||||
|
# FuncGen._member_base 的查询前补齐使用(与跨模块路径同一口径)。
|
||||||
|
Gen._members_flattened.add(ClassName)
|
||||||
# _create_Vtable_globals 会跳过正在发射中的类(方法列表可能不完整),
|
# _create_Vtable_globals 会跳过正在发射中的类(方法列表可能不完整),
|
||||||
# 当前类的方法已全部注册(L559-571),此处显式创建其 vtable
|
# 当前类的方法已全部注册(L559-571),此处显式创建其 vtable
|
||||||
if IsCVTable:
|
if IsCVTable:
|
||||||
@@ -1333,11 +1385,10 @@ class ClassHandle(BaseHandle):
|
|||||||
if union_member_types:
|
if union_member_types:
|
||||||
max_size: int = 0
|
max_size: int = 0
|
||||||
for member_type in union_member_types:
|
for member_type in union_member_types:
|
||||||
try:
|
# 统一走 Gen._get_struct_size(与 __sizeof__ / memhub alloc 同源)。
|
||||||
size: int = member_type.get_abi_size(ir.DataLayout(Gen.module.data_layout))
|
# llvmlite 并无 ir.DataLayout 类,原先那行必然抛 AttributeError,
|
||||||
except Exception as e: # 回退:获取类型大小失败时使用默认值 8
|
# 再被 except 吞成 size=8 —— 静默少算,union 载荷区被开得过小。
|
||||||
_vlog().warning(f"HandlesClassDef: 忽略异常 {e}", exc_info=e)
|
size: int = Gen._get_struct_size(member_type)
|
||||||
size = 8
|
|
||||||
max_size = max(max_size, size)
|
max_size = max(max_size, size)
|
||||||
union_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName)
|
union_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName)
|
||||||
union_type.set_body(ir.ArrayType(ir.IntType(8), max_size))
|
union_type.set_body(ir.ArrayType(ir.IntType(8), max_size))
|
||||||
@@ -1498,12 +1549,85 @@ class ClassHandle(BaseHandle):
|
|||||||
# 更新预注册的 REnum 类型信息(填充变体列表)
|
# 更新预注册的 REnum 类型信息(填充变体列表)
|
||||||
PreTypeInfo.RenumVariants = variant_names
|
PreTypeInfo.RenumVariants = variant_names
|
||||||
|
|
||||||
|
def _EnumMemberAnnotationIsPtr(self, annotation: ast.AST | None) -> bool:
|
||||||
|
"""枚举成员注解是否含指针形态(逐层下标 + 联合类型逐边),True=含指针
|
||||||
|
|
||||||
|
与 TPV 侧 ClassGenEnum._enum_member_annotation_is_ptr 同口径。枚举值是
|
||||||
|
整数常量,指针成员语义不成立,必须明确报错(fail-fast)而非静默接受:
|
||||||
|
此前 TPC 完全忽略成员注解,`Name: t.CPtr` 会被当成普通 i32 成员放行。
|
||||||
|
指针标记既按名字匹配(含 str/bytes = i8*、rawptr = t.CPtr),也按
|
||||||
|
from t import CPtr as P 这类别名经 _t_type_symbols 还原后判定。
|
||||||
|
"""
|
||||||
|
if annotation is None:
|
||||||
|
return False
|
||||||
|
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
|
||||||
|
return (self._EnumMemberAnnotationIsPtr(annotation.left)
|
||||||
|
or self._EnumMemberAnnotationIsPtr(annotation.right))
|
||||||
|
if isinstance(annotation, ast.Subscript):
|
||||||
|
return self._EnumMemberAnnotationIsPtr(annotation.value)
|
||||||
|
name: str | None = None
|
||||||
|
if isinstance(annotation, ast.Attribute):
|
||||||
|
name = annotation.attr
|
||||||
|
elif isinstance(annotation, ast.Name):
|
||||||
|
name = annotation.id
|
||||||
|
if name is None:
|
||||||
|
return False
|
||||||
|
if name in _ENUM_PTR_ANNOTATION_NAMES:
|
||||||
|
return True
|
||||||
|
symtab: object = self.Trans.SymbolTable
|
||||||
|
resolved: type | None = None
|
||||||
|
if hasattr(symtab, '_t_type_symbols'):
|
||||||
|
resolved = symtab._t_type_symbols.get(name)
|
||||||
|
if resolved is None:
|
||||||
|
resolved = getattr(t, name, None)
|
||||||
|
if isinstance(resolved, type) and issubclass(resolved, t.CPtr):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _EnumClassIntWidth(self, Node: ast.ClassDef) -> int:
|
||||||
|
"""按"最宽成员类型"推导枚举存储宽度(bit)。
|
||||||
|
|
||||||
|
规则本体唯一:SymbolUtils.EnumClassIntWidth(定义端 / 跨模块 `.vpi` 读取端 /
|
||||||
|
符号表提取端 / 声明端共用同一函数),此处只注入本端 SymbolTable
|
||||||
|
(覆盖库内自定义 typedef 与 builtins 表)。无任何可解析为整数的成员
|
||||||
|
→ 默认 32——既有枚举全部落在此支,与改前的恒 i32 逐字一致(零行为变化)。
|
||||||
|
"""
|
||||||
|
return EnumClassIntWidth(Node.body, self.Trans.SymbolTable)
|
||||||
|
|
||||||
|
def _EnumSiteLoc(self, Node: ast.AST) -> str:
|
||||||
|
"""枚举 fail-fast 站点:`文件:行[:列]`(行号经 `_line_offset` 还原)+ 源码片段。
|
||||||
|
|
||||||
|
口径与 `_FailFastFixedArrayKeyword` / `SymbolUtils._DefineSiteInfo` 一致。
|
||||||
|
"""
|
||||||
|
Gen: object = getattr(self.Trans, 'LlvmGen', None)
|
||||||
|
lineno: int = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
Offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if Offset > 0 and lineno > Offset:
|
||||||
|
lineno -= Offset
|
||||||
|
col: int = int(getattr(Node, 'col_offset', -1))
|
||||||
|
Loc: str = getattr(Gen, '_current_source_file', '') or ''
|
||||||
|
Loc += f":{lineno}"
|
||||||
|
if col >= 0:
|
||||||
|
Loc += f":{col}"
|
||||||
|
Source: str = ''
|
||||||
|
if lineno > 0 and Gen is not None and hasattr(Gen, '_get_source_line'):
|
||||||
|
try:
|
||||||
|
Source = (Gen._get_source_line(lineno) or '').strip()
|
||||||
|
except Exception:
|
||||||
|
Source = ''
|
||||||
|
return Loc + (f",源码: {Source}" if Source else '')
|
||||||
|
|
||||||
def _RegisterEnumMembers(self, Node: ast.ClassDef) -> None:
|
def _RegisterEnumMembers(self, Node: ast.ClassDef) -> None:
|
||||||
ClassName: str = Node.name
|
ClassName: str = Node.name
|
||||||
|
# 枚举存储宽度 = 最宽成员类型(先推导再登记;成员注册顺序无关)。
|
||||||
|
# 登记到枚举条目与每个成员条目上,供常量发射点与类型解析点同点读取
|
||||||
|
# (两侧 ABI:常量宽度必须与枚举类型宽度一致)。
|
||||||
|
EnumWidth: int = self._EnumClassIntWidth(Node)
|
||||||
EnumTypeInfo: CTypeInfo = CTypeInfo()
|
EnumTypeInfo: CTypeInfo = CTypeInfo()
|
||||||
EnumTypeInfo.Name = ClassName
|
EnumTypeInfo.Name = ClassName
|
||||||
EnumTypeInfo.BaseType = t.CEnum(ClassName)
|
EnumTypeInfo.BaseType = t.CEnum(ClassName)
|
||||||
EnumTypeInfo.IsEnum = True
|
EnumTypeInfo.IsEnum = True
|
||||||
|
EnumTypeInfo.set('enum_width', EnumWidth)
|
||||||
self.Trans.SymbolTable.insert(ClassName, EnumTypeInfo)
|
self.Trans.SymbolTable.insert(ClassName, EnumTypeInfo)
|
||||||
enum_export = self.Trans.Exportable.add_enum(
|
enum_export = self.Trans.Exportable.add_enum(
|
||||||
name=ClassName,
|
name=ClassName,
|
||||||
@@ -1511,6 +1635,8 @@ class ClassHandle(BaseHandle):
|
|||||||
is_public=True
|
is_public=True
|
||||||
)
|
)
|
||||||
next_enum_value: int = 0
|
next_enum_value: int = 0
|
||||||
|
# 已登记成员名(用于"可见名数 vs 登记数"兜底判据,见 SymbolUtils.EnumBodyMemberNames)
|
||||||
|
RegisteredNames: list[str] = []
|
||||||
for item in Node.body:
|
for item in Node.body:
|
||||||
if isinstance(item, ast.Assign):
|
if isinstance(item, ast.Assign):
|
||||||
for target in item.targets:
|
for target in item.targets:
|
||||||
@@ -1541,7 +1667,9 @@ class ClassHandle(BaseHandle):
|
|||||||
MemberNode.EnumName = ClassName
|
MemberNode.EnumName = ClassName
|
||||||
MemberNode.Lineno = item.lineno
|
MemberNode.Lineno = item.lineno
|
||||||
MemberNode.IsEnumMember = True
|
MemberNode.IsEnumMember = True
|
||||||
|
MemberNode.set('enum_width', EnumWidth)
|
||||||
self.Trans.SymbolTable.insert(VarName, MemberNode)
|
self.Trans.SymbolTable.insert(VarName, MemberNode)
|
||||||
|
RegisteredNames.append(VarName)
|
||||||
enum_export.members.append(ExportEnumMember(
|
enum_export.members.append(ExportEnumMember(
|
||||||
name=VarName,
|
name=VarName,
|
||||||
value=value,
|
value=value,
|
||||||
@@ -1550,6 +1678,19 @@ class ClassHandle(BaseHandle):
|
|||||||
elif isinstance(item, ast.AnnAssign):
|
elif isinstance(item, ast.AnnAssign):
|
||||||
if isinstance(item.target, ast.Name):
|
if isinstance(item.target, ast.Name):
|
||||||
VarName = item.target.id
|
VarName = item.target.id
|
||||||
|
# [枚举规格 fail-fast] 成员不允许指针类型(t.CPtr 家族 /
|
||||||
|
# rawptr[...] / str / bytes / 联合里夹带指针)——此前成员注解
|
||||||
|
# 被整体忽略,指针成员被静默当 i32 放行。
|
||||||
|
if self._EnumMemberAnnotationIsPtr(item.annotation):
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(item, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
|
raise TypeError(
|
||||||
|
f"[fast-fail] 枚举 '{ClassName}' 成员 '{VarName}' 不能是指针类型"
|
||||||
|
f"(枚举值只支持整数成员类型),line {_lineno}"
|
||||||
|
)
|
||||||
value = None
|
value = None
|
||||||
if item.value:
|
if item.value:
|
||||||
if isinstance(item.value, ast.Constant):
|
if isinstance(item.value, ast.Constant):
|
||||||
@@ -1575,12 +1716,33 @@ class ClassHandle(BaseHandle):
|
|||||||
MemberNode.EnumName = ClassName
|
MemberNode.EnumName = ClassName
|
||||||
MemberNode.Lineno = item.lineno
|
MemberNode.Lineno = item.lineno
|
||||||
MemberNode.IsEnumMember = True
|
MemberNode.IsEnumMember = True
|
||||||
|
MemberNode.set('enum_width', EnumWidth)
|
||||||
self.Trans.SymbolTable.insert(VarName, MemberNode)
|
self.Trans.SymbolTable.insert(VarName, MemberNode)
|
||||||
|
RegisteredNames.append(VarName)
|
||||||
enum_export.members.append(ExportEnumMember(
|
enum_export.members.append(ExportEnumMember(
|
||||||
name=VarName,
|
name=VarName,
|
||||||
value=value,
|
value=value,
|
||||||
lineno=item.lineno
|
lineno=item.lineno
|
||||||
))
|
))
|
||||||
|
# [兜底判据 fail-fast] "源码里可见的成员名字数" vs "实际登记成员数"。
|
||||||
|
# 任何注册器不认的成员写法(逗号组带值 `A, B = 1`、逗号组带注解
|
||||||
|
# `A, B: T`、单名尾随逗号 `A,`,以及未来出现的新形态)都会让可见名数 >
|
||||||
|
# 登记数——此前是**静默丢成员**(ASTKind 71→3、OpKind 32→2;与
|
||||||
|
# Debugger/enum-semantics.md §15、rawptr-migration-and-fastfail.md §27
|
||||||
|
# 同族,这是第三次复发),此处一律 fail-fast,不再静默跳过。
|
||||||
|
# 判据口径与 TPV ClassGenEnum._translate_enum_def 完全一致(同一规则源)。
|
||||||
|
Visible: list[tuple[str, object]] = EnumBodyMemberNames(Node.body)
|
||||||
|
if len(Visible) != len(RegisteredNames):
|
||||||
|
Rest: list[str] = list(RegisteredNames)
|
||||||
|
BadNode: object = Node
|
||||||
|
for Nm, Nd in Visible:
|
||||||
|
if Nm in Rest:
|
||||||
|
Rest.remove(Nm)
|
||||||
|
elif BadNode is Node:
|
||||||
|
BadNode = Nd
|
||||||
|
raise TypeError(EnumMemberCountMismatchError(
|
||||||
|
ClassName, Visible, RegisteredNames, self._EnumSiteLoc(BadNode),
|
||||||
|
int(getattr(self.Trans, '_line_offset', 0) or 0)))
|
||||||
|
|
||||||
def _EmitNewFunctionLlvm(self, ClassName: str, Gen: LlvmGeneratorMixin, IsCVTable: bool = False, IsCpythonObject: bool = False) -> None:
|
def _EmitNewFunctionLlvm(self, ClassName: str, Gen: LlvmGeneratorMixin, IsCVTable: bool = False, IsCpythonObject: bool = False) -> None:
|
||||||
NewFuncName: str = f'{ClassName}.__before_init__'
|
NewFuncName: str = f'{ClassName}.__before_init__'
|
||||||
|
|||||||
@@ -9,6 +9,19 @@ import llvmlite.ir as ir
|
|||||||
from lib.core.Handles.HandlesBase import BaseHandle
|
from lib.core.Handles.HandlesBase import BaseHandle
|
||||||
from lib.constants.config import mode as _config_mode
|
from lib.constants.config import mode as _config_mode
|
||||||
from lib.includes.t import CTypeRegistry as _CR
|
from lib.includes.t import CTypeRegistry as _CR
|
||||||
|
from lib.core.SymbolUtils import RequireDefineUseWidthAt, RequireIntLiteralStorable, RequireNameDefined
|
||||||
|
|
||||||
|
|
||||||
|
def DefineUseWidthFromLLVMType(Ty: object) -> int | None:
|
||||||
|
"""LLVM 类型 → CDefine 常量的"使用处位宽"提示;其余 → None。
|
||||||
|
|
||||||
|
只认 i8/i16/i32/i64(与 SymbolUtils.DEFINE_WIDTH_LADDER 同集合):
|
||||||
|
i1(比较/逻辑结果)与指针不构成"整数使用处宽度",必须落回"取不到 ⇒
|
||||||
|
默认 32/64",否则 `C == 0` 之类会拿到位宽 1 而误报"使用处宽度不足"。
|
||||||
|
"""
|
||||||
|
if isinstance(Ty, ir.IntType) and Ty.width in (8, 16, 32, 64):
|
||||||
|
return int(Ty.width)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class ExprHandle(BaseHandle):
|
class ExprHandle(BaseHandle):
|
||||||
@@ -45,14 +58,18 @@ class ExprHandle(BaseHandle):
|
|||||||
def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None:
|
def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None:
|
||||||
return self.Trans.ExprAttrHandle._get_llvm_member_offset(field_name, ClassName, Gen)
|
return self.Trans.ExprAttrHandle._get_llvm_member_offset(field_name, ClassName, Gen)
|
||||||
|
|
||||||
def HandleExprLlvm(self, Node: ast.AST, VarType: ir.Type | str | None = None) -> ir.Value | None:
|
def HandleExprLlvm(self, Node: ast.AST, VarType: ir.Type | str | None = None,
|
||||||
|
ConstWidthHint: int | None = None) -> ir.Value | None:
|
||||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||||
if not Gen or not Gen.builder:
|
if not Gen or not Gen.builder:
|
||||||
return None
|
return None
|
||||||
if isinstance(Node, ast.Constant):
|
if isinstance(Node, ast.Constant):
|
||||||
return self._HandleConstantLlvm(Node, VarType)
|
return self._HandleConstantLlvm(Node, VarType)
|
||||||
elif isinstance(Node, ast.Name):
|
elif isinstance(Node, ast.Name):
|
||||||
return self._HandleNameLlvm(Node, VarType)
|
# ConstWidthHint 是**窄通道**:只有 Name(CDefine 常量物化)读取它,
|
||||||
|
# 其余分支(字面量/列表/运算…)逐字沿用既有 VarType 语义 ⇒ 非常量
|
||||||
|
# 路径的既有 IR 不变。
|
||||||
|
return self._HandleNameLlvm(Node, VarType, ConstWidthHint)
|
||||||
elif isinstance(Node, ast.BinOp):
|
elif isinstance(Node, ast.BinOp):
|
||||||
return self.Trans.ExprOpsHandle._HandleBinOpLlvm(Node, VarType)
|
return self.Trans.ExprOpsHandle._HandleBinOpLlvm(Node, VarType)
|
||||||
elif isinstance(Node, ast.BoolOp):
|
elif isinstance(Node, ast.BoolOp):
|
||||||
@@ -161,25 +178,30 @@ class ExprHandle(BaseHandle):
|
|||||||
elif elem_type == 'double':
|
elif elem_type == 'double':
|
||||||
byte_offset = i * 8
|
byte_offset = i * 8
|
||||||
|
|
||||||
ptr_as_int: Any = Gen.builder.ptrtoint(cast_ptr, ir.IntType(64))
|
# 元素地址:以 i8* 基址按字节 GEP,再转成元素指针类型
|
||||||
new_ptr_val: Any = Gen.builder.add(ptr_as_int, ir.Constant(ir.IntType(64), byte_offset))
|
# (不再 ptr→uint64 手动加偏移再 inttoptr,见新规范第 10 条)
|
||||||
|
elem_addr: Any = Gen.builder.gep(cast_ptr, [ir.Constant(ir.IntType(64), byte_offset)], name=f"elem_addr_{i}")
|
||||||
if elem_type == 'i32':
|
if elem_type == 'i32':
|
||||||
elem_ptr: Any = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(32)))
|
elem_ptr: Any = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(32)))
|
||||||
elif elem_type == 'i16':
|
elif elem_type == 'i16':
|
||||||
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(16)))
|
elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(16)))
|
||||||
elif elem_type == 'float':
|
elif elem_type == 'float':
|
||||||
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.FloatType()))
|
elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.FloatType()))
|
||||||
elif elem_type == 'double':
|
elif elem_type == 'double':
|
||||||
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.DoubleType()))
|
elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.DoubleType()))
|
||||||
else:
|
else:
|
||||||
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(8)))
|
elem_ptr = elem_addr
|
||||||
|
|
||||||
if elem_type == 'i8':
|
if elem_type == 'i8':
|
||||||
if isinstance(elem_val.type, ir.IntType) and elem_val.type.width > 8:
|
if isinstance(elem_val.type, ir.IntType) and elem_val.type.width > 8:
|
||||||
elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}")
|
elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}")
|
||||||
elif isinstance(elem_val.type, ir.PointerType):
|
elif isinstance(elem_val.type, ir.PointerType):
|
||||||
elem_val = Gen.builder.ptrtoint(elem_val, ir.IntType(64), name=f"ptrtoint_elem_{i}")
|
# 指针 → 整数:隐式转换一律禁止(元素值是指针、元素类型是整数)
|
||||||
elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}")
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type},"
|
||||||
|
f"但目标元素类型是 i8;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
|
)
|
||||||
elif elem_type == 'i32':
|
elif elem_type == 'i32':
|
||||||
if isinstance(elem_val.type, ir.IntType):
|
if isinstance(elem_val.type, ir.IntType):
|
||||||
if elem_val.type.width < 32:
|
if elem_val.type.width < 32:
|
||||||
@@ -187,7 +209,12 @@ class ExprHandle(BaseHandle):
|
|||||||
elif elem_val.type.width > 32:
|
elif elem_val.type.width > 32:
|
||||||
elem_val = Gen.builder.trunc(elem_val, ir.IntType(32), name=f"trunc_elem_{i}")
|
elem_val = Gen.builder.trunc(elem_val, ir.IntType(32), name=f"trunc_elem_{i}")
|
||||||
elif isinstance(elem_val.type, ir.PointerType):
|
elif isinstance(elem_val.type, ir.PointerType):
|
||||||
elem_val = Gen.builder.ptrtoint(elem_val, ir.IntType(32), name=f"ptrtoint_elem_{i}")
|
# 指针 → 整数:隐式转换一律禁止(ptrtoint 到 i32 还会截断地址)
|
||||||
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type},"
|
||||||
|
f"但目标元素类型是 i32;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
|
)
|
||||||
elif elem_type == 'i16':
|
elif elem_type == 'i16':
|
||||||
if isinstance(elem_val.type, ir.IntType):
|
if isinstance(elem_val.type, ir.IntType):
|
||||||
if elem_val.type.width < 16:
|
if elem_val.type.width < 16:
|
||||||
@@ -221,6 +248,10 @@ class ExprHandle(BaseHandle):
|
|||||||
elif isinstance(Value, bool):
|
elif isinstance(Value, bool):
|
||||||
return ir.Constant(ir.IntType(32), 1 if Value else 0)
|
return ir.Constant(ir.IntType(32), 1 if Value else 0)
|
||||||
elif isinstance(Value, int):
|
elif isinstance(Value, int):
|
||||||
|
# 整数字面量越界 fail-fast(字面量本身无符号,0..2^64-1):与 TPV 词法层
|
||||||
|
# `_lex_int_exceeds_u64` 同口径。负数侧由"一元减 + 字面量"的联合判定负责
|
||||||
|
# (HandlesExprOps._HandleUnaryOpLlvm),此处只判字面量本身。
|
||||||
|
RequireIntLiteralStorable(Gen, self.Trans, Node, Value)
|
||||||
if VarType is not None:
|
if VarType is not None:
|
||||||
if isinstance(VarType, ir.IntType):
|
if isinstance(VarType, ir.IntType):
|
||||||
return ir.Constant(VarType, Value & ((1 << VarType.width) - 1))
|
return ir.Constant(VarType, Value & ((1 << VarType.width) - 1))
|
||||||
@@ -327,7 +358,8 @@ class ExprHandle(BaseHandle):
|
|||||||
return ptr
|
return ptr
|
||||||
return ir.Constant(ir.IntType(32), 0)
|
return ir.Constant(ir.IntType(32), 0)
|
||||||
|
|
||||||
def _HandleNameLlvm(self, Node: ast.Name, VarType: ir.Type | str | None = None) -> ir.Value:
|
def _HandleNameLlvm(self, Node: ast.Name, VarType: ir.Type | str | None = None,
|
||||||
|
ConstWidthHint: int | None = None) -> ir.Value:
|
||||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||||
VarName: str = Node.id
|
VarName: str = Node.id
|
||||||
# 治本修复:不把 module.globals 的 initializer 注入 _define_constants。
|
# 治本修复:不把 module.globals 的 initializer 注入 _define_constants。
|
||||||
@@ -343,6 +375,9 @@ class ExprHandle(BaseHandle):
|
|||||||
# 跳过 _define_constants 查询,直接走下方 variables/global_vars/module.globals 的 load。
|
# 跳过 _define_constants 查询,直接走下方 variables/global_vars/module.globals 的 load。
|
||||||
if VarName in define_constants and not (hasattr(Gen, 'module') and VarName in Gen.module.globals):
|
if VarName in define_constants and not (hasattr(Gen, 'module') and VarName in Gen.module.globals):
|
||||||
val: Any = define_constants[VarName]
|
val: Any = define_constants[VarName]
|
||||||
|
# CDefine 常量注册值(用于使用处宽度判据;下方 _gvar 覆盖是"同名全局变量"
|
||||||
|
# 的既有特例,不应把变量值当常量做宽度判据)
|
||||||
|
_cdefine_val: Any = val
|
||||||
# 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过
|
# 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过
|
||||||
# 共享 _define_constants 覆盖本模块常量
|
# 共享 _define_constants 覆盖本模块常量
|
||||||
# (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug)
|
# (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug)
|
||||||
@@ -357,9 +392,28 @@ class ExprHandle(BaseHandle):
|
|||||||
if isinstance(_init, ir.Constant) and isinstance(_init.constant, (int, float, str)):
|
if isinstance(_init, ir.Constant) and isinstance(_init.constant, (int, float, str)):
|
||||||
val = _init.constant
|
val = _init.constant
|
||||||
if isinstance(val, int):
|
if isinstance(val, int):
|
||||||
# Use VarType hint if available to avoid unnecessary type mismatch
|
# 使用处宽度(唯一判据 SymbolUtils.RequireDefineUseWidth):
|
||||||
if VarType is not None and isinstance(VarType, ir.IntType):
|
# 上下文(赋值目标 / 形参 / 返回注解 / 运算对象…)给出的位宽更宽 ⇒
|
||||||
return ir.Constant(VarType, val & ((1 << VarType.width) - 1))
|
# 就地按该宽度物化(立即数升宽无损);更窄 ⇒ fail-fast(替换此前的
|
||||||
|
# `val & mask` 静默截断)。
|
||||||
|
# 宽度来源优先级:**窄通道 ConstWidthHint**(仅常量物化读取,由调用
|
||||||
|
# 装配处按形参/返回类型下传)> 既有尽宽通道 VarType(赋值目标 /
|
||||||
|
# 二元运算对象)。两者都取不到 ⇒ 落到下方既有 32/64 二值口径。
|
||||||
|
_use_ty: ir.Type | None = None
|
||||||
|
# 报文的"使用处"标签按**通道**取真实站点种类(不再写笼统的
|
||||||
|
# "由调用方下传的类型提示"):窄通道 ConstWidthHint 只由
|
||||||
|
# 调用装配处(形参)与返回处下传;既有 VarType 通道承载的是
|
||||||
|
# 赋值目标 / 二元运算对象类型。
|
||||||
|
_use_ctx: str = '赋值目标 / 运算对象类型'
|
||||||
|
if ConstWidthHint in (8, 16, 32, 64):
|
||||||
|
_use_ty = ir.IntType(int(ConstWidthHint))
|
||||||
|
_use_ctx = '形参 / 返回注解'
|
||||||
|
elif VarType is not None and isinstance(VarType, ir.IntType):
|
||||||
|
_use_ty = VarType
|
||||||
|
if isinstance(_use_ty, ir.IntType):
|
||||||
|
RequireDefineUseWidthAt(Gen, self.Trans, Node, _cdefine_val,
|
||||||
|
int(_use_ty.width), VarName, _use_ctx)
|
||||||
|
return ir.Constant(_use_ty, val & ((1 << _use_ty.width) - 1))
|
||||||
if VarName in Gen.var_signedness and Gen.var_signedness[VarName]:
|
if VarName in Gen.var_signedness and Gen.var_signedness[VarName]:
|
||||||
if val < -(1 << 31) or val > 0xFFFFFFFF:
|
if val < -(1 << 31) or val > 0xFFFFFFFF:
|
||||||
return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF)
|
return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF)
|
||||||
@@ -384,8 +438,20 @@ class ExprHandle(BaseHandle):
|
|||||||
if VarName in all_dc:
|
if VarName in all_dc:
|
||||||
val: Any = all_dc[VarName]
|
val: Any = all_dc[VarName]
|
||||||
if isinstance(val, int):
|
if isinstance(val, int):
|
||||||
if VarType is not None and isinstance(VarType, ir.IntType):
|
# 跨模块 CDefine 常量同口径:窄通道 ConstWidthHint(形参/返回
|
||||||
return ir.Constant(VarType, val & ((1 << VarType.width) - 1))
|
# 类型下传)优先,其次既有 VarType;更宽 ⇒ 按该宽度物化,
|
||||||
|
# 更窄 ⇒ fail-fast(判据唯一源 SymbolUtils.RequireDefineUseWidth)
|
||||||
|
_use_ty2: ir.Type | None = None
|
||||||
|
_use_ctx2: str = '赋值目标 / 运算对象类型'
|
||||||
|
if ConstWidthHint in (8, 16, 32, 64):
|
||||||
|
_use_ty2 = ir.IntType(int(ConstWidthHint))
|
||||||
|
_use_ctx2 = '形参 / 返回注解'
|
||||||
|
elif VarType is not None and isinstance(VarType, ir.IntType):
|
||||||
|
_use_ty2 = VarType
|
||||||
|
if isinstance(_use_ty2, ir.IntType):
|
||||||
|
RequireDefineUseWidthAt(Gen, self.Trans, Node, val,
|
||||||
|
int(_use_ty2.width), VarName, _use_ctx2)
|
||||||
|
return ir.Constant(_use_ty2, val & ((1 << _use_ty2.width) - 1))
|
||||||
if val < -(1 << 31) or val > 0xFFFFFFFF:
|
if val < -(1 << 31) or val > 0xFFFFFFFF:
|
||||||
return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF)
|
return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF)
|
||||||
return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF)
|
return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF)
|
||||||
@@ -460,7 +526,7 @@ class ExprHandle(BaseHandle):
|
|||||||
SymInfo: Any = self.Trans.SymbolTable.lookup(VarName)
|
SymInfo: Any = self.Trans.SymbolTable.lookup(VarName)
|
||||||
if SymInfo:
|
if SymInfo:
|
||||||
if SymInfo.IsEnumMember and isinstance(SymInfo.value, int):
|
if SymInfo.IsEnumMember and isinstance(SymInfo.value, int):
|
||||||
return ir.Constant(ir.IntType(32), SymInfo.value)
|
return ir.Constant(Gen._enum_int_type(SymInfo.EnumName, SymInfo), SymInfo.value)
|
||||||
if SymInfo.IsFunction or SymInfo.IsFuncPtr:
|
if SymInfo.IsFunction or SymInfo.IsFuncPtr:
|
||||||
MangledName: str = Gen._mangle_func_name(VarName)
|
MangledName: str = Gen._mangle_func_name(VarName)
|
||||||
if MangledName in Gen.module.globals:
|
if MangledName in Gen.module.globals:
|
||||||
@@ -487,4 +553,7 @@ class ExprHandle(BaseHandle):
|
|||||||
)
|
)
|
||||||
ast.copy_location(virtual_attr, Node)
|
ast.copy_location(virtual_attr, Node)
|
||||||
return self.Trans.ExprAttrHandle._HandleAttributeLlvm(virtual_attr)
|
return self.Trans.ExprAttrHandle._HandleAttributeLlvm(virtual_attr)
|
||||||
|
# fail-fast: 未定义名称禁止静默物化为 i32 0(会把常量名写错 / define 丢失 /
|
||||||
|
# 变量漏声明这类上游缺陷藏到运行期,见 RequireNameDefined 的头注释)。
|
||||||
|
RequireNameDefined(Gen, self.Trans, Node, VarName)
|
||||||
return ir.Constant(ir.IntType(32), 0)
|
return ir.Constant(ir.IntType(32), 0)
|
||||||
@@ -201,6 +201,34 @@ class ExprAsmHandle(BaseHandle):
|
|||||||
stripped.append('')
|
stripped.append('')
|
||||||
return '\n'.join([first_line] + stripped)
|
return '\n'.join([first_line] + stripped)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _intrinsic_marker(expr: ast.AST) -> str | None:
|
||||||
|
"""识别内建操作数标记调用,返回规范名或 None
|
||||||
|
|
||||||
|
支持两种等价写法(裸名写法省去 c. 前缀):
|
||||||
|
- 内联汇编:c.AsmInp / c.AsmOut ↔ asminp / asmout
|
||||||
|
- LLVM IR:c.LInp / c.LOut ↔ irinp / irout
|
||||||
|
"""
|
||||||
|
if not isinstance(expr, ast.Call):
|
||||||
|
return None
|
||||||
|
func: ast.AST = expr.func
|
||||||
|
if isinstance(func, ast.Name):
|
||||||
|
if func.id == 'asminp':
|
||||||
|
return 'AsmInp'
|
||||||
|
if func.id == 'asmout':
|
||||||
|
return 'AsmOut'
|
||||||
|
if func.id == 'irinp':
|
||||||
|
return 'LInp'
|
||||||
|
if func.id == 'irout':
|
||||||
|
return 'LOut'
|
||||||
|
return None
|
||||||
|
if isinstance(func, ast.Attribute):
|
||||||
|
if isinstance(func.value, ast.Name) and func.value.id == 'c':
|
||||||
|
if func.attr in ('AsmInp', 'AsmOut', 'LInp', 'LOut'):
|
||||||
|
return func.attr
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
def _process_joined_str_template(self, node: ast.AST, out_input_ops: list[Any], out_input_constraints: list[str],
|
def _process_joined_str_template(self, node: ast.AST, out_input_ops: list[Any], out_input_constraints: list[str],
|
||||||
out_output_ops: list[Any], out_output_constraints: list[str]) -> str:
|
out_output_ops: list[Any], out_output_constraints: list[str]) -> str:
|
||||||
if not isinstance(node, ast.JoinedStr):
|
if not isinstance(node, ast.JoinedStr):
|
||||||
@@ -214,39 +242,40 @@ class ExprAsmHandle(BaseHandle):
|
|||||||
raw_parts.append(('text', value.value))
|
raw_parts.append(('text', value.value))
|
||||||
elif isinstance(value, ast.FormattedValue):
|
elif isinstance(value, ast.FormattedValue):
|
||||||
expr: ast.AST = value.value
|
expr: ast.AST = value.value
|
||||||
if isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute):
|
_marker: str | None = self._intrinsic_marker(expr)
|
||||||
if isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c':
|
if _marker == 'AsmInp':
|
||||||
if expr.func.attr == 'AsmInp':
|
if expr.args:
|
||||||
if expr.args:
|
v: Any = self.HandleExprLlvm(expr.args[0])
|
||||||
v: Any = self.HandleExprLlvm(expr.args[0])
|
if v:
|
||||||
if v:
|
c: str = 'r'
|
||||||
c: str = 'r'
|
if len(expr.args) >= 2:
|
||||||
if len(expr.args) >= 2:
|
c = self._parse_asm_descr(expr.args[1])
|
||||||
c = self._parse_asm_descr(expr.args[1])
|
if not c:
|
||||||
if not c:
|
c = 'r'
|
||||||
c = 'r'
|
out_input_ops.append(v)
|
||||||
out_input_ops.append(v)
|
out_input_constraints.append(c)
|
||||||
out_input_constraints.append(c)
|
raw_parts.append(('input', len(out_input_ops) - 1))
|
||||||
raw_parts.append(('input', len(out_input_ops) - 1))
|
elif _marker == 'AsmOut':
|
||||||
elif expr.func.attr == 'AsmOut':
|
if expr.args:
|
||||||
if expr.args:
|
target_ptr: Any = self._get_var_ptr_for_asm(expr.args[0])
|
||||||
target_ptr: Any = self._get_var_ptr_for_asm(expr.args[0])
|
if target_ptr:
|
||||||
if target_ptr:
|
v = target_ptr
|
||||||
v = target_ptr
|
else:
|
||||||
else:
|
v = self.HandleExprLlvm(expr.args[0])
|
||||||
v = self.HandleExprLlvm(expr.args[0])
|
if v:
|
||||||
if v:
|
c: str = '=r'
|
||||||
c: str = '=r'
|
if len(expr.args) >= 2:
|
||||||
if len(expr.args) >= 2:
|
c = self._parse_asm_descr(expr.args[1])
|
||||||
c = self._parse_asm_descr(expr.args[1])
|
if not c:
|
||||||
if not c:
|
c = '=r'
|
||||||
c = '=r'
|
if not c.startswith('='):
|
||||||
if not c.startswith('='):
|
c = '=' + c
|
||||||
c = '=' + c
|
out_output_ops.append(v)
|
||||||
out_output_ops.append(v)
|
out_output_constraints.append(c)
|
||||||
out_output_constraints.append(c)
|
raw_parts.append(('output', len(out_output_ops) - 1))
|
||||||
raw_parts.append(('output', len(out_output_ops) - 1))
|
elif not (isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute)):
|
||||||
else:
|
# 非 c.* 属性调用的表达式按普通输入操作数处理(保持原行为:
|
||||||
|
# c.* 属性调用中非 AsmInp/AsmOut 的形态不收集)
|
||||||
fallback: Any = self.HandleExprLlvm(expr)
|
fallback: Any = self.HandleExprLlvm(expr)
|
||||||
if fallback and isinstance(fallback.type, ir.IntType):
|
if fallback and isinstance(fallback.type, ir.IntType):
|
||||||
out_input_ops.append(fallback)
|
out_input_ops.append(fallback)
|
||||||
@@ -316,25 +345,24 @@ class ExprAsmHandle(BaseHandle):
|
|||||||
|
|
||||||
if output_arg and isinstance(output_arg, (ast.List, ast.Tuple)):
|
if output_arg and isinstance(output_arg, (ast.List, ast.Tuple)):
|
||||||
for item in output_arg.elts:
|
for item in output_arg.elts:
|
||||||
if isinstance(item, ast.Call) and isinstance(item.func, ast.Attribute):
|
if self._intrinsic_marker(item) == 'AsmOut':
|
||||||
if item.func.value.id == 'c' and item.func.attr == 'AsmOut':
|
if len(item.args) >= 1:
|
||||||
if len(item.args) >= 1:
|
target_ptr: Any = self._get_var_ptr_for_asm(item.args[0])
|
||||||
target_ptr: Any = self._get_var_ptr_for_asm(item.args[0])
|
if target_ptr:
|
||||||
if target_ptr:
|
output_ops.append(target_ptr)
|
||||||
output_ops.append(target_ptr)
|
else:
|
||||||
else:
|
value: Any = self.HandleExprLlvm(item.args[0])
|
||||||
value: Any = self.HandleExprLlvm(item.args[0])
|
if value:
|
||||||
if value:
|
output_ops.append(value)
|
||||||
output_ops.append(value)
|
constraint: str = '=r'
|
||||||
constraint: str = '=r'
|
if len(item.args) >= 2:
|
||||||
if len(item.args) >= 2:
|
base_constraint: str = self._parse_asm_descr(item.args[1])
|
||||||
base_constraint: str = self._parse_asm_descr(item.args[1])
|
if base_constraint:
|
||||||
if base_constraint:
|
if base_constraint.startswith('='):
|
||||||
if base_constraint.startswith('='):
|
constraint = base_constraint
|
||||||
constraint = base_constraint
|
else:
|
||||||
else:
|
constraint = '=' + base_constraint
|
||||||
constraint = '=' + base_constraint
|
output_constraints.append(constraint)
|
||||||
output_constraints.append(constraint)
|
|
||||||
|
|
||||||
raw_template: str
|
raw_template: str
|
||||||
if isinstance(asm_template_arg, ast.JoinedStr):
|
if isinstance(asm_template_arg, ast.JoinedStr):
|
||||||
@@ -360,18 +388,17 @@ class ExprAsmHandle(BaseHandle):
|
|||||||
|
|
||||||
if input_arg and isinstance(input_arg, (ast.List, ast.Tuple)):
|
if input_arg and isinstance(input_arg, (ast.List, ast.Tuple)):
|
||||||
for item in input_arg.elts:
|
for item in input_arg.elts:
|
||||||
if isinstance(item, ast.Call) and isinstance(item.func, ast.Attribute):
|
if self._intrinsic_marker(item) == 'AsmInp':
|
||||||
if item.func.value.id == 'c' and item.func.attr == 'AsmInp':
|
if len(item.args) >= 1:
|
||||||
if len(item.args) >= 1:
|
value: Any = self.HandleExprLlvm(item.args[0])
|
||||||
value: Any = self.HandleExprLlvm(item.args[0])
|
if value:
|
||||||
if value:
|
input_ops.append(value)
|
||||||
input_ops.append(value)
|
constraint: str = 'r'
|
||||||
constraint: str = 'r'
|
if len(item.args) >= 2:
|
||||||
if len(item.args) >= 2:
|
constraint = self._parse_asm_descr(item.args[1])
|
||||||
constraint = self._parse_asm_descr(item.args[1])
|
if not constraint:
|
||||||
if not constraint:
|
constraint = 'r'
|
||||||
constraint = 'r'
|
input_constraints.append(constraint)
|
||||||
input_constraints.append(constraint)
|
|
||||||
elif isinstance(item, (ast.List, ast.Tuple)) and len(item.elts) >= 2:
|
elif isinstance(item, (ast.List, ast.Tuple)) and len(item.elts) >= 2:
|
||||||
value_node: ast.AST = item.elts[0]
|
value_node: ast.AST = item.elts[0]
|
||||||
descr_node: ast.AST = item.elts[1]
|
descr_node: ast.AST = item.elts[1]
|
||||||
@@ -565,7 +592,8 @@ class ExprAsmHandle(BaseHandle):
|
|||||||
expr.value.attr == 'ASM_DESCR'):
|
expr.value.attr == 'ASM_DESCR'):
|
||||||
AttrName: str = expr.attr
|
AttrName: str = expr.attr
|
||||||
return getattr(t.ASM_DESCR, AttrName, "")
|
return getattr(t.ASM_DESCR, AttrName, "")
|
||||||
elif isinstance(expr.value, ast.Name) and IsTModule(expr.value.id, self.Trans.SymbolTable):
|
elif isinstance(expr.value, ast.Name) and (expr.value.id == 'ASM_DESCR' or IsTModule(expr.value.id, self.Trans.SymbolTable)):
|
||||||
|
# 裸名 ASM_DESCR.XXX:省去 t. 前缀的等价写法
|
||||||
AttrName: str = expr.attr
|
AttrName: str = expr.attr
|
||||||
return getattr(t.ASM_DESCR, AttrName, "")
|
return getattr(t.ASM_DESCR, AttrName, "")
|
||||||
|
|
||||||
|
|||||||
@@ -199,6 +199,32 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
return self._make_define_constant(Gen, define_constants[key])
|
return self._make_define_constant(Gen, define_constants[key])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _NormalizeModuleGlobalValue(self, Gen: LlvmCodeGenerator, Val: Any, AttrName: str) -> Any:
|
||||||
|
"""模块限定全局读取归一化:类指针全局的「多一层」元素类型还原为对象指针
|
||||||
|
|
||||||
|
`Mod.g`(g 为模块级全局)的取值统一经 _lookup_module_global:注解为
|
||||||
|
`X | t.CPtr` 的模块级全局(如 GenApi._g_gen: rawptr[Generator.Generator]、
|
||||||
|
VLogger.logger: Logger | t.CPtr)在本编译器里物化成「比对象多一层」的
|
||||||
|
元素类型 T**(写入端见 main.vp `GenApi._g_gen = gen`:bitcast T*→T** 后
|
||||||
|
直接存值,未取地址)⇒ 其位形就是对象指针 T*,静态类型多了一层。
|
||||||
|
裸名形态(`g.Field`)由 _handle_attr_name 的既有同款补偿 bitcast 处理;
|
||||||
|
模块限定形态(`Mod.g.Field`)此前无补偿 ⇒ 载入值停在 T**,其 pointee 是
|
||||||
|
T*(指针而非结构体)⇒ _handle_attr_nested 的 char* 分支与 struct 分支都
|
||||||
|
不命中而返回 None ⇒ 带注解赋值 RHS 求值为空 fail-fast
|
||||||
|
(App/lib/core/Repl.vp:763 `GenApi._g_gen.TypeGenCDef.CDefineNames`)、
|
||||||
|
调用接收者 None 而静默丢调用(VpiReader.vp:4066
|
||||||
|
`GenApi._g_gen.ClassGenBase._collect_parents(...)`)。此处做同款逆运算:
|
||||||
|
只改静态类型、不改位形(bitcast 不产指令)。
|
||||||
|
判据限定「pointee 仍是指向结构体的指针」⇒ `str | t.CPtr` 一类真双层指针
|
||||||
|
全局(@_argv / @_g_pyi_cdef_names = i8**,实参直传 i8** 形参)不受影响。
|
||||||
|
"""
|
||||||
|
if not isinstance(Val, ir.Value) or not isinstance(Val.type, ir.PointerType):
|
||||||
|
return Val
|
||||||
|
Inner: Any = Val.type.pointee
|
||||||
|
if isinstance(Inner, ir.PointerType) and isinstance(Inner.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
|
return Gen.builder.bitcast(Val, Inner, name=f"cast_{AttrName}_global")
|
||||||
|
return Val
|
||||||
|
|
||||||
def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None:
|
def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None:
|
||||||
"""在模块全局变量中查找 import 的属性"""
|
"""在模块全局变量中查找 import 的属性"""
|
||||||
imported_modules: Any = getattr(self.Trans, '_ImportedModules', None)
|
imported_modules: Any = getattr(self.Trans, '_ImportedModules', None)
|
||||||
@@ -220,7 +246,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
return GVar
|
return GVar
|
||||||
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||||||
return GVar
|
return GVar
|
||||||
return Gen._load(GVar, name=AttrName)
|
return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar, name=AttrName), AttrName)
|
||||||
# SHA1 前缀查找
|
# SHA1 前缀查找
|
||||||
ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {})
|
ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {})
|
||||||
sha1_candidates: list[str] = [VarName, resolved_mod]
|
sha1_candidates: list[str] = [VarName, resolved_mod]
|
||||||
@@ -238,7 +264,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
return GVar2
|
return GVar2
|
||||||
if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||||||
return GVar2
|
return GVar2
|
||||||
return Gen._load(GVar2, name=AttrName)
|
return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar2, name=AttrName), AttrName)
|
||||||
if AttrName in Gen.module.globals:
|
if AttrName in Gen.module.globals:
|
||||||
gv: Any = Gen.module.globals[AttrName]
|
gv: Any = Gen.module.globals[AttrName]
|
||||||
if AttrName in Gen.variables and Gen.variables[AttrName] is None:
|
if AttrName in Gen.variables and Gen.variables[AttrName] is None:
|
||||||
@@ -246,7 +272,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
if str_gv_name in Gen.module.globals:
|
if str_gv_name in Gen.module.globals:
|
||||||
str_gv: Any = Gen.module.globals[str_gv_name]
|
str_gv: Any = Gen.module.globals[str_gv_name]
|
||||||
return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr")
|
return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr")
|
||||||
return Gen.builder.load(gv, name=AttrName)
|
return self._NormalizeModuleGlobalValue(Gen, Gen.builder.load(gv, name=AttrName), AttrName)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None:
|
def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None:
|
||||||
@@ -373,6 +399,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
|
|
||||||
def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None:
|
def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None:
|
||||||
"""处理枚举成员访问 (VarName 是枚举类型名)"""
|
"""处理枚举成员访问 (VarName 是枚举类型名)"""
|
||||||
|
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||||
SymInfo: Any = self.Trans.SymbolTable.lookup(VarName)
|
SymInfo: Any = self.Trans.SymbolTable.lookup(VarName)
|
||||||
if not SymInfo or not SymInfo.IsEnum:
|
if not SymInfo or not SymInfo.IsEnum:
|
||||||
return None
|
return None
|
||||||
@@ -387,10 +414,10 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
for key, info in self.Trans.SymbolTable.items():
|
for key, info in self.Trans.SymbolTable.items():
|
||||||
if info.IsEnumMember and info.EnumName == VarName and key == Node.attr:
|
if info.IsEnumMember and info.EnumName == VarName and key == Node.attr:
|
||||||
if isinstance(info.value, int):
|
if isinstance(info.value, int):
|
||||||
return ir.Constant(ir.IntType(32), info.value)
|
return ir.Constant(Gen._enum_int_type(VarName, info), info.value)
|
||||||
return None
|
return None
|
||||||
if isinstance(MemberInfo.value, int):
|
if isinstance(MemberInfo.value, int):
|
||||||
return ir.Constant(ir.IntType(32), MemberInfo.value)
|
return ir.Constant(Gen._enum_int_type(VarName, MemberInfo), MemberInfo.value)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None:
|
def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None:
|
||||||
@@ -520,12 +547,12 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"):
|
for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"):
|
||||||
info: Any = self.Trans.SymbolTable.lookup(qname)
|
info: Any = self.Trans.SymbolTable.lookup(qname)
|
||||||
if info and info.IsEnumMember and isinstance(info.value, int):
|
if info and info.IsEnumMember and isinstance(info.value, int):
|
||||||
return ir.Constant(ir.IntType(32), info.value)
|
return ir.Constant(Gen._enum_int_type(ClassName, info), info.value)
|
||||||
for key, info in self.Trans.SymbolTable.items():
|
for key, info in self.Trans.SymbolTable.items():
|
||||||
if key == AttrName:
|
if key == AttrName:
|
||||||
if info.IsEnumMember and info.EnumName == ClassName:
|
if info.IsEnumMember and info.EnumName == ClassName:
|
||||||
if isinstance(info.value, int):
|
if isinstance(info.value, int):
|
||||||
return ir.Constant(ir.IntType(32), info.value)
|
return ir.Constant(Gen._enum_int_type(ClassName, info), info.value)
|
||||||
break
|
break
|
||||||
|
|
||||||
# CUnion 处理
|
# CUnion 处理
|
||||||
@@ -1105,6 +1132,11 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
|
|
||||||
def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None:
|
def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None:
|
||||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||||
|
# pointee-volatile(rawptr[t.CVolatile | T])基址:下标 GEP 名带基址名,
|
||||||
|
# 顶层 _load/_store 经 BaseGen._IsPtrVolatile 回查登记名,使其 load/store
|
||||||
|
# 带 LLVM volatile。基址名取不到时后缀为空 ⇒ GEP 名仍是 "subscript",
|
||||||
|
# 既有代码 IR 逐字节不变。
|
||||||
|
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
|
||||||
# 查询数组元素字节序
|
# 查询数组元素字节序
|
||||||
arr_byte_order: str = ''
|
arr_byte_order: str = ''
|
||||||
if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders:
|
if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders:
|
||||||
@@ -1178,7 +1210,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
|
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
|
||||||
ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
|
ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8:
|
if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8:
|
||||||
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
||||||
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
||||||
@@ -1191,7 +1223,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
elem_type: Any = pointee.element
|
elem_type: Any = pointee.element
|
||||||
if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
@@ -1210,14 +1242,14 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str")
|
CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str")
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index")
|
||||||
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
return Gen._load(ElemPtr, name="str_ptr_load")
|
return Gen._load(ElemPtr, name="str_ptr_load")
|
||||||
elem_class: str | None = self._infer_element_struct_class(Node.value, Gen)
|
elem_class: str | None = self._infer_element_struct_class(Node.value, Gen)
|
||||||
if elem_class and elem_class in Gen.structs:
|
if elem_class and elem_class in Gen.structs:
|
||||||
CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr")
|
CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr")
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
if isinstance(pointee, ir.IntType):
|
if isinstance(pointee, ir.IntType):
|
||||||
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
@@ -1230,7 +1262,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
CastedPtrPtr = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str")
|
CastedPtrPtr = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str")
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index")
|
||||||
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
return Gen._load(ElemPtr, name="str_ptr_load")
|
return Gen._load(ElemPtr, name="str_ptr_load")
|
||||||
arr_type: Any = self._infer_array_member_type(Node.value, Gen)
|
arr_type: Any = self._infer_array_member_type(Node.value, Gen)
|
||||||
if arr_type and isinstance(arr_type, ir.ArrayType):
|
if arr_type and isinstance(arr_type, ir.ArrayType):
|
||||||
@@ -1241,12 +1273,12 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
arr_elem_type: Any = arr_type.element
|
arr_elem_type: Any = arr_type.element
|
||||||
if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
||||||
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
||||||
@@ -1262,7 +1294,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
var_arr_elem_type: Any = VarPtr.type.pointee.element
|
var_arr_elem_type: Any = VarPtr.type.pointee.element
|
||||||
if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
@@ -1275,7 +1307,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
attr_arr_elem_type: Any = AttrPtr.type.pointee.element
|
attr_arr_elem_type: Any = AttrPtr.type.pointee.element
|
||||||
if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
@@ -1290,14 +1322,14 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
inner_elem_type: Any = inner_pointee.element
|
inner_elem_type: Any = inner_pointee.element
|
||||||
if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
|
||||||
arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
||||||
Gen._store(ValueVal, arr_alloc)
|
Gen._store(ValueVal, arr_alloc)
|
||||||
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
alloc_arr_elem_type: Any = ValueVal.type.element
|
alloc_arr_elem_type: Any = ValueVal.type.element
|
||||||
if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
@@ -1306,6 +1338,8 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
|
|
||||||
def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None:
|
def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None:
|
||||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||||
|
# 同 _HandleSubscriptLlvm:pointee-volatile 基址名后缀(空后缀 ⇒ 名不变)
|
||||||
|
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
|
||||||
ValueVal: Any = self.HandleExprLlvm(Node.value)
|
ValueVal: Any = self.HandleExprLlvm(Node.value)
|
||||||
if not ValueVal:
|
if not ValueVal:
|
||||||
return None
|
return None
|
||||||
@@ -1324,12 +1358,12 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript_ptr")
|
ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
elif isinstance(pointee, ir.PointerType):
|
elif isinstance(pointee, ir.PointerType):
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_ptr")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_ptr")
|
||||||
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript_ptr")
|
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
else:
|
else:
|
||||||
# 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr, bytes | t.CPtr)
|
# 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr, bytes | t.CPtr)
|
||||||
@@ -1341,11 +1375,11 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr")
|
CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr")
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr")
|
||||||
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript_ptr")
|
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
|
||||||
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript_ptr")
|
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
elif isinstance(ValueVal.type, ir.IntType):
|
elif isinstance(ValueVal.type, ir.IntType):
|
||||||
var_ptr: Any = self._get_int_ptr(Node.value)
|
var_ptr: Any = self._get_int_ptr(Node.value)
|
||||||
@@ -1367,7 +1401,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript_ptr")
|
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
if isinstance(Node.value, ast.Subscript):
|
if isinstance(Node.value, ast.Subscript):
|
||||||
InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value)
|
InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value)
|
||||||
@@ -1377,7 +1411,7 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript_ptr")
|
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
if isinstance(Node.value, ast.Attribute):
|
if isinstance(Node.value, ast.Attribute):
|
||||||
AttrPtr: Any = self._get_attr_ptr(Node.value)
|
AttrPtr: Any = self._get_attr_ptr(Node.value)
|
||||||
@@ -1387,11 +1421,11 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript_ptr")
|
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
||||||
Gen._store(ValueVal, arr_alloc)
|
Gen._store(ValueVal, arr_alloc)
|
||||||
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript_ptr")
|
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -1705,17 +1739,17 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"):
|
for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"):
|
||||||
info: Any = self.Trans.SymbolTable.lookup(qname)
|
info: Any = self.Trans.SymbolTable.lookup(qname)
|
||||||
if info and info.IsEnumMember and isinstance(info.value, int):
|
if info and info.IsEnumMember and isinstance(info.value, int):
|
||||||
return ir.Constant(ir.IntType(32), info.value)
|
return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
|
||||||
for key, info in self.Trans.SymbolTable.items():
|
for key, info in self.Trans.SymbolTable.items():
|
||||||
if info.IsEnumMember and info.EnumName == enum_key and key == member_name:
|
if info.IsEnumMember and info.EnumName == enum_key and key == member_name:
|
||||||
if isinstance(info.value, int):
|
if isinstance(info.value, int):
|
||||||
return ir.Constant(ir.IntType(32), info.value)
|
return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
|
||||||
for key, info in self.Trans.SymbolTable.items():
|
for key, info in self.Trans.SymbolTable.items():
|
||||||
if info.IsEnumMember and key == member_name:
|
if info.IsEnumMember and key == member_name:
|
||||||
if isinstance(info.value, int):
|
if isinstance(info.value, int):
|
||||||
for enum_key in enum_keys:
|
for enum_key in enum_keys:
|
||||||
if info.EnumName == enum_key:
|
if info.EnumName == enum_key:
|
||||||
return ir.Constant(ir.IntType(32), info.value)
|
return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None:
|
def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None:
|
||||||
|
|||||||
@@ -727,7 +727,12 @@ class ExprBuiltinHandle(BaseHandle):
|
|||||||
elif isinstance(Node, ast.Attribute):
|
elif isinstance(Node, ast.Attribute):
|
||||||
type_name = Node.attr
|
type_name = Node.attr
|
||||||
else:
|
else:
|
||||||
return ir.Constant(ir.IntType(64), 0)
|
# 非 名称/属性 的 sizeof 目标(如 sizeof(a + b)、sizeof(t.CArray[int,4])):
|
||||||
|
# 曾静默返回 0 → 下游 alloc(0) 返回 NULL。fail-fast。
|
||||||
|
raise ValueError(
|
||||||
|
f"[FATAL][__sizeof__] sizeof 的目标 {type(Node).__name__}"
|
||||||
|
f"({ast.unparse(Node)})不是类型名(Name/Attribute),拒绝静默返回 0"
|
||||||
|
)
|
||||||
|
|
||||||
if type_name not in Gen.structs:
|
if type_name not in Gen.structs:
|
||||||
# str/bytes 是指针类型 (char*/void*),sizeof = 指针大小
|
# str/bytes 是指针类型 (char*/void*),sizeof = 指针大小
|
||||||
@@ -758,7 +763,11 @@ class ExprBuiltinHandle(BaseHandle):
|
|||||||
if _config_mode == "strict":
|
if _config_mode == "strict":
|
||||||
raise
|
raise
|
||||||
_vlog().warning(f"解析类型失败: {_e}", "Exception")
|
_vlog().warning(f"解析类型失败: {_e}", "Exception")
|
||||||
return ir.Constant(ir.IntType(64), 0)
|
# 所有查找策略均失败:曾静默返回 0 → 下游 elem_size=0 / alloc(0)。fail-fast。
|
||||||
|
raise ValueError(
|
||||||
|
f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析"
|
||||||
|
f"(不在 Gen.structs、CTypeRegistry 也找不到),拒绝静默返回 0"
|
||||||
|
)
|
||||||
|
|
||||||
struct_type = Gen.structs[type_name]
|
struct_type = Gen.structs[type_name]
|
||||||
if isinstance(struct_type, ir.PointerType):
|
if isinstance(struct_type, ir.PointerType):
|
||||||
@@ -768,10 +777,8 @@ class ExprBuiltinHandle(BaseHandle):
|
|||||||
struct_type = Gen.structs.get(type_name, struct_type)
|
struct_type = Gen.structs.get(type_name, struct_type)
|
||||||
# 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确
|
# 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确
|
||||||
self._resolve_nested_opaque_structs(struct_type, Gen)
|
self._resolve_nested_opaque_structs(struct_type, Gen)
|
||||||
size = Gen._get_struct_size(struct_type)
|
# _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。
|
||||||
if size > 0:
|
return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type))
|
||||||
return ir.Constant(ir.IntType(64), size)
|
|
||||||
return ir.Constant(ir.IntType(64), 0)
|
|
||||||
|
|
||||||
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
|
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
|
||||||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||||||
|
|||||||
+495
-162
@@ -14,7 +14,9 @@ from lib.includes import t
|
|||||||
from lib.includes.t import CTypeRegistry
|
from lib.includes.t import CTypeRegistry
|
||||||
from lib.core.SymbolUtils import (
|
from lib.core.SymbolUtils import (
|
||||||
IsTModule, ExtractTypeNameFromBinOp, IsPtrSubscriptNode, DesugarPtrSubscriptToUnion,
|
IsTModule, ExtractTypeNameFromBinOp, IsPtrSubscriptNode, DesugarPtrSubscriptToUnion,
|
||||||
ResolveTypeArgStr, GenericArgRawName)
|
ResolveTypeArgStr, GenericArgRawName,
|
||||||
|
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
|
||||||
|
from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType
|
||||||
|
|
||||||
|
|
||||||
def _safe_ptr_type(ty: ir.Type) -> ir.Type:
|
def _safe_ptr_type(ty: ir.Type) -> ir.Type:
|
||||||
@@ -236,6 +238,71 @@ class ExprCallHandle(BaseHandle):
|
|||||||
except (AssertionError, TypeError):
|
except (AssertionError, TypeError):
|
||||||
CallArgs.append(ir.Constant(ir.IntType(32), 0))
|
CallArgs.append(ir.Constant(ir.IntType(32), 0))
|
||||||
|
|
||||||
|
def _LookupGlobalDefaults(self, func_name: str | None) -> list | None:
|
||||||
|
"""按函数名查询全局默认参数表(跨模块调用点用)。
|
||||||
|
|
||||||
|
查找顺序:精确键 → 去 mangled 前缀后的裸名 → 以 '.<裸名>' 结尾的键
|
||||||
|
(如 {sha1}.name)。精确键优先,避免"同名不同模块"的默认值互相乱配。
|
||||||
|
"""
|
||||||
|
if not func_name:
|
||||||
|
return None
|
||||||
|
table: dict = getattr(self.Trans, '_global_function_default_args', None) or {}
|
||||||
|
if not table:
|
||||||
|
return None
|
||||||
|
hit = table.get(func_name)
|
||||||
|
if hit:
|
||||||
|
return hit
|
||||||
|
base: str = func_name.split('.')[-1]
|
||||||
|
if base != func_name:
|
||||||
|
hit = table.get(base)
|
||||||
|
if hit:
|
||||||
|
return hit
|
||||||
|
for key, value in table.items():
|
||||||
|
if key.endswith('.' + base):
|
||||||
|
return value
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _LookupGlobalParamNames(self, func_name: str | None) -> list | None:
|
||||||
|
"""按函数名查询全局参数名表;查找顺序同 _LookupGlobalDefaults。"""
|
||||||
|
if not func_name:
|
||||||
|
return None
|
||||||
|
table: dict = getattr(self.Trans, '_global_function_param_names', None) or {}
|
||||||
|
if not table:
|
||||||
|
return None
|
||||||
|
hit = table.get(func_name)
|
||||||
|
if hit:
|
||||||
|
return hit
|
||||||
|
base: str = func_name.split('.')[-1]
|
||||||
|
if base != func_name:
|
||||||
|
hit = table.get(base)
|
||||||
|
if hit:
|
||||||
|
return hit
|
||||||
|
for key, value in table.items():
|
||||||
|
if key.endswith('.' + base):
|
||||||
|
return value
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _ConstantToIrValue(raw: object, param_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Value | None:
|
||||||
|
"""把全局默认参数表里的 Python 常量转成形参类型的 IR 常量。
|
||||||
|
|
||||||
|
只处理整数/布尔(跨模块默认值里最常见,如 cap: t.CSizeT = 4096)。字符串等
|
||||||
|
常量需要在调用点引用定义模块的全局对象,无法安全重建 → 返回 None,调用点
|
||||||
|
退回原有零值填充(与修复前行为一致)。
|
||||||
|
"""
|
||||||
|
if isinstance(param_type, ir.IntType):
|
||||||
|
if isinstance(raw, bool):
|
||||||
|
raw = 1 if raw else 0
|
||||||
|
if isinstance(raw, int):
|
||||||
|
max_val: int = (1 << param_type.width) - 1
|
||||||
|
if raw > max_val or raw < 0:
|
||||||
|
raw = raw & max_val
|
||||||
|
return ir.Constant(param_type, raw)
|
||||||
|
return ir.Constant(param_type, 0)
|
||||||
|
if isinstance(param_type, ir.PointerType):
|
||||||
|
return ir.Constant(param_type, None)
|
||||||
|
return None
|
||||||
|
|
||||||
def _ApplyKeywordArgs(self, Node: ast.Call, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None, self_offset: int = 1) -> None:
|
def _ApplyKeywordArgs(self, Node: ast.Call, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None, self_offset: int = 1) -> None:
|
||||||
"""在 _fill_default_args 之后调用:用 keyword args 按 param_names 覆盖 CallArgs 对应位置。
|
"""在 _fill_default_args 之后调用:用 keyword args 按 param_names 覆盖 CallArgs 对应位置。
|
||||||
self_offset=1 表示 CallArgs[0] 是 self,参数从 CallArgs[1] 开始(__init__/__new__/__call__/method)。
|
self_offset=1 表示 CallArgs[0] 是 self,参数从 CallArgs[1] 开始(__init__/__new__/__call__/method)。
|
||||||
@@ -274,6 +341,108 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if 0 <= target_idx < len(CallArgs) - self_offset:
|
if 0 <= target_idx < len(CallArgs) - self_offset:
|
||||||
CallArgs[self_offset + target_idx] = ArgVal
|
CallArgs[self_offset + target_idx] = ArgVal
|
||||||
|
|
||||||
|
def _CheckFuncPtrArity(self, Node: ast.Call, FnPtr: ir.Value, Args: list[ir.Value],
|
||||||
|
FuncName: str, Gen: LlvmGeneratorMixin) -> None:
|
||||||
|
"""函数指针间接调用前的 arity 校验(fail-fast,紧邻 builder.call 之前)。
|
||||||
|
|
||||||
|
期望形参个数取自 FnPtr 的静态 LLVM 函数类型的形参表:来源是函数指针槽
|
||||||
|
(模块级/局部 t.Callable[...] 注解)的 CTypeInfo.FuncPtrParams →
|
||||||
|
CTypeInfo.ToLLVM/_ctype_to_llvm → ir.FunctionType(ret, params);变参位由
|
||||||
|
FunctionType.var_arg 承载。不校验时,实参个数少于形参个数会让 llvmlite
|
||||||
|
CallInstr 按形参个数索引 args[i] 抛裸 `IndexError: list index out of range`
|
||||||
|
(无函数名、无源位置,栈顶落在被调函数 docstring 行,无法定位真因)。
|
||||||
|
|
||||||
|
变参函数(var_arg=True,如 C printf 形态)只要求实参不少于固定形参个数:
|
||||||
|
多出的实参按变参传递,不报错;仅 len(Args) < 固定形参个数 时报错。
|
||||||
|
"""
|
||||||
|
FnType: ir.Type = FnPtr.type.pointee if isinstance(FnPtr.type, ir.PointerType) else FnPtr.type
|
||||||
|
if not isinstance(FnType, ir.FunctionType):
|
||||||
|
return
|
||||||
|
Expected: int = len(FnType.args)
|
||||||
|
Actual: int = len(Args)
|
||||||
|
IsVariadic: bool = bool(getattr(FnType, 'var_arg', False))
|
||||||
|
if IsVariadic:
|
||||||
|
if Actual >= Expected:
|
||||||
|
return
|
||||||
|
elif Actual == Expected:
|
||||||
|
return
|
||||||
|
# 源位置:按本文件既有写法取 Node 自身的 lineno/col_offset(不用
|
||||||
|
# _current_node_info/_current_lineno —— 二者可能停留在上一条语句),
|
||||||
|
# 源文件取 Gen._current_source_file,源码行取 Gen._get_source_line。
|
||||||
|
Lineno: int = getattr(Node, 'lineno', 0) or 0
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
|
||||||
|
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator);
|
||||||
|
# 报文行号与源码行必须同用还原后的 Lineno,二者一致(否则会显示下一行源码)。
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and Lineno > _line_offset:
|
||||||
|
Lineno -= _line_offset
|
||||||
|
Col: int = getattr(Node, 'col_offset', -1)
|
||||||
|
SrcFile: str = getattr(Gen, '_current_source_file', '') or 'unknown'
|
||||||
|
Loc: str = f"{SrcFile}:{Lineno}"
|
||||||
|
if Col >= 0:
|
||||||
|
Loc += f":{Col}"
|
||||||
|
SourceLine: str = ''
|
||||||
|
if Lineno > 0 and hasattr(Gen, '_get_source_line'):
|
||||||
|
SourceLine = (Gen._get_source_line(Lineno) or '').strip()
|
||||||
|
ExpectText: str = (f"至少 {Expected} 个形参(变参函数,仅固定形参)"
|
||||||
|
if IsVariadic else f" {Expected} 个形参")
|
||||||
|
raise Exception(
|
||||||
|
f"[TransPyC] 函数指针调用实参个数不匹配: '{FuncName}' 期望{ExpectText},"
|
||||||
|
f"实际传入 {Actual} 个实参;请检查该函数指针(t.Callable[...] 注解槽)的"
|
||||||
|
f"形参表是否与调用处实参个数一致。位置 {Loc}"
|
||||||
|
f"{(',源码: ' + SourceLine) if SourceLine else ''}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _RequireArgValue(self, ArgVal: ir.Value | None, Callee: str, ArgIndex: int,
|
||||||
|
ArgNode: ast.AST, CallNode: ast.Call) -> ir.Value:
|
||||||
|
"""fail-fast:调用实参编译期求值为空(`HandleExprLlvm` 返回 None)时终止装配。
|
||||||
|
|
||||||
|
调用装配处原写作 `ArgVal = self.HandleExprLlvm(arg); if ArgVal: 追加(ArgVal)`:
|
||||||
|
实参求值为 None 时它被**静默整条丢弃**,导致后续实参整体前移一格,最终由
|
||||||
|
`FuncGen._adjust_args` 报出误导性的"指针类型不能隐式转换为整数参数"(真因是
|
||||||
|
"少了一个实参")。实例:`ast.OpKind.BitOr` 因枚举成员未登记而求值 None ⇒
|
||||||
|
`ast.BinOp(...)` 的 4 个实参退化为 3 个 ⇒ `AST*` 落到 `op: i32`(见
|
||||||
|
`Debugger/rawptr-migration-and-fastfail.md` §27、`Debugger/enum-semantics.md`
|
||||||
|
的「逗号分组成员被静默丢弃」一节)。
|
||||||
|
|
||||||
|
`HandleExprLlvm` 返回 None 只有一种语义——该表达式编译期**求值失败**
|
||||||
|
(名称/属性链/枚举成员未登记、跨模块符号查找失败、类构造在声明期结构体不完整等);
|
||||||
|
"按类型本无值"的形态(以返回 void 的调用收尾)返回的是 CallInstr 对象而非 None,
|
||||||
|
故不存在需要跳过的合法实参。此处改为当场终止,报文含被调者、实参序号与源表达式、
|
||||||
|
根因提示与源码位置;**不丢弃该实参**。
|
||||||
|
"""
|
||||||
|
if ArgVal is not None:
|
||||||
|
return ArgVal
|
||||||
|
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||||||
|
expr_text: str = self._ExprText(ArgNode)
|
||||||
|
ArgKind: str = type(ArgNode).__name__
|
||||||
|
raw_lineno: int = getattr(ArgNode, 'lineno', 0) or getattr(CallNode, 'lineno', 0) or 0
|
||||||
|
lineno: int = raw_lineno
|
||||||
|
# 行号还原:`_inject_auto_imports` 会在解析前往文件头注入 import 行,AST 行号
|
||||||
|
# 比磁盘真实行号大 N;与 HandlesBase._RaiseUnresolvedExpr 同口径。
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and lineno > _line_offset:
|
||||||
|
lineno -= _line_offset
|
||||||
|
col: int = getattr(ArgNode, 'col_offset', -1)
|
||||||
|
src_file: str = getattr(Gen, '_current_source_file', '') or 'unknown'
|
||||||
|
code_line: str = ''
|
||||||
|
if lineno > 0 and hasattr(Gen, '_get_source_line'):
|
||||||
|
try:
|
||||||
|
code_line = (Gen._get_source_line(lineno) or '').strip()
|
||||||
|
except Exception:
|
||||||
|
code_line = ''
|
||||||
|
loc: str = f"{src_file}:{lineno}"
|
||||||
|
if col >= 0:
|
||||||
|
loc += f":{col}"
|
||||||
|
raise Exception(
|
||||||
|
f"[TransPyC] 调用实参编译期求值为空: 被调者 '{Callee}' 的第 {ArgIndex} 个实参 "
|
||||||
|
f"'{expr_text}'({ArgKind} 形态)求值失败;已终止编译,不丢弃该实参"
|
||||||
|
f"(丢弃会使后续实参前移一格,报出与真因无关的类型不匹配)。"
|
||||||
|
f"常见根因:枚举成员/名称未登记、跨模块符号查找失败、类定义在声明期结构体不完整。"
|
||||||
|
f"位置 {loc}"
|
||||||
|
f"{(',源码: ' + code_line) if code_line else ''}"
|
||||||
|
)
|
||||||
|
|
||||||
def _HandleCallLlvm(self, Node: ast.Call) -> ir.Value:
|
def _HandleCallLlvm(self, Node: ast.Call) -> ir.Value:
|
||||||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||||||
# 协程:coro.coro_run(帧) 内联翻译为 resume 循环
|
# 协程:coro.coro_run(帧) 内联翻译为 resume 循环
|
||||||
@@ -300,6 +469,15 @@ class ExprCallHandle(BaseHandle):
|
|||||||
ModulePath: str | None = None
|
ModulePath: str | None = None
|
||||||
if isinstance(Node.func, ast.Name):
|
if isinstance(Node.func, ast.Name):
|
||||||
FuncName = Node.func.id
|
FuncName = Node.func.id
|
||||||
|
# __asm__(...) 语法糖:等价 c.Asm(...)(裸名调用,无需 import c)
|
||||||
|
if FuncName == '__asm__':
|
||||||
|
return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node)
|
||||||
|
# __llvmir__(...) 语法糖:等价 c.LLVMIR(...)
|
||||||
|
if FuncName == '__llvmir__':
|
||||||
|
return self._HandleLLVMIRLlvm(Node)
|
||||||
|
# irinp/irout 等价 c.LInp/c.LOut(仅作模板占位符,独立调用返回 0)
|
||||||
|
if FuncName in ('irinp', 'irout'):
|
||||||
|
return ir.Constant(ir.IntType(32), 0)
|
||||||
# M1 协程:普通函数调用 async 函数 → create_<name>()(返回帧指针)
|
# M1 协程:普通函数调用 async 函数 → create_<name>()(返回帧指针)
|
||||||
if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(FuncName):
|
if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(FuncName):
|
||||||
return self.Trans.AsyncHandler._HandleAsyncCreateCallLlvm(Node, FuncName)
|
return self.Trans.AsyncHandler._HandleAsyncCreateCallLlvm(Node, FuncName)
|
||||||
@@ -316,16 +494,19 @@ class ExprCallHandle(BaseHandle):
|
|||||||
# 若该名字同时是类(在 Gen.structs 中或存在 __before_init__),优先作为类构造函数处理
|
# 若该名字同时是类(在 Gen.structs 中或存在 __before_init__),优先作为类构造函数处理
|
||||||
IsAlsoClass: bool = (FuncName in Gen.structs) or Gen._has_function(f'{FuncName}.__before_init__')
|
IsAlsoClass: bool = (FuncName in Gen.structs) or Gen._has_function(f'{FuncName}.__before_init__')
|
||||||
if SymInfo.IsEnum and not (IsAlsoClass and getattr(SymInfo, 'IsEnumMember', False)):
|
if SymInfo.IsEnum and not (IsAlsoClass and getattr(SymInfo, 'IsEnumMember', False)):
|
||||||
|
# 枚举类型转换 `EnumName(v)`:结果宽度 = 该枚举的存储宽度
|
||||||
|
# (最宽成员类型),与成员常量宽度同点(ABI 一致)。
|
||||||
|
_enum_ty: ir.IntType = Gen._enum_int_type(FuncName, SymInfo)
|
||||||
if Node.args:
|
if Node.args:
|
||||||
arg_val = self.HandleExprLlvm(Node.args[0])
|
arg_val = self.HandleExprLlvm(Node.args[0])
|
||||||
if arg_val:
|
if arg_val:
|
||||||
if isinstance(arg_val.type, ir.IntType) and arg_val.type.width != 32:
|
if isinstance(arg_val.type, ir.IntType) and arg_val.type.width != _enum_ty.width:
|
||||||
if arg_val.type.width < 32:
|
if arg_val.type.width < _enum_ty.width:
|
||||||
arg_val = Gen.builder.zext(arg_val, ir.IntType(32), name="zext_enum_arg")
|
arg_val = Gen.builder.zext(arg_val, _enum_ty, name="zext_enum_arg")
|
||||||
else:
|
else:
|
||||||
arg_val = Gen.builder.trunc(arg_val, ir.IntType(32), name="trunc_enum_arg")
|
arg_val = Gen.builder.trunc(arg_val, _enum_ty, name="trunc_enum_arg")
|
||||||
return arg_val
|
return arg_val
|
||||||
return ir.Constant(ir.IntType(32), 0)
|
return ir.Constant(_enum_ty, 0)
|
||||||
if SymInfo.IsTypedef:
|
if SymInfo.IsTypedef:
|
||||||
return self._HandleTypedefCastLlvm(Node, FuncName)
|
return self._HandleTypedefCastLlvm(Node, FuncName)
|
||||||
NewFuncName = f'{FuncName}.__before_init__'
|
NewFuncName = f'{FuncName}.__before_init__'
|
||||||
@@ -370,6 +551,7 @@ class ExprCallHandle(BaseHandle):
|
|||||||
ArgVal = self.HandleExprLlvm(arg)
|
ArgVal = self.HandleExprLlvm(arg)
|
||||||
if ArgVal:
|
if ArgVal:
|
||||||
args.append(ArgVal)
|
args.append(ArgVal)
|
||||||
|
self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen)
|
||||||
return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}")
|
return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}")
|
||||||
closure_ptr = Gen._load(VarPtr, name=f"Load_closure_{FuncName}")
|
closure_ptr = Gen._load(VarPtr, name=f"Load_closure_{FuncName}")
|
||||||
if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.FunctionType):
|
if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.FunctionType):
|
||||||
@@ -379,6 +561,7 @@ class ExprCallHandle(BaseHandle):
|
|||||||
ArgVal = self.HandleExprLlvm(arg)
|
ArgVal = self.HandleExprLlvm(arg)
|
||||||
if ArgVal:
|
if ArgVal:
|
||||||
args.append(ArgVal)
|
args.append(ArgVal)
|
||||||
|
self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen)
|
||||||
return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}")
|
return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}")
|
||||||
# 处理 t.CPtr (i8*) 函数指针调用
|
# 处理 t.CPtr (i8*) 函数指针调用
|
||||||
# 当函数被赋值给 t.CPtr 变量时,原始函数类型退化为 i8*
|
# 当函数被赋值给 t.CPtr 变量时,原始函数类型退化为 i8*
|
||||||
@@ -449,13 +632,39 @@ class ExprCallHandle(BaseHandle):
|
|||||||
safe_env_type: ir.Type = _safe_ptr_type(expected_fn_type.args[0])
|
safe_env_type: ir.Type = _safe_ptr_type(expected_fn_type.args[0])
|
||||||
call_args[0] = Gen.builder.bitcast(call_args[0], safe_env_type, name=f"cast_env_{FuncName}")
|
call_args[0] = Gen.builder.bitcast(call_args[0], safe_env_type, name=f"cast_env_{FuncName}")
|
||||||
return Gen.builder.call(fn_val, call_args, name=f"call_{FuncName}")
|
return Gen.builder.call(fn_val, call_args, name=f"call_{FuncName}")
|
||||||
|
# 编译器内置裸指针强转 rawptr(x):rawptr 是 TPC/TPV 双端**语法保留字**
|
||||||
|
# (无 SymbolTable typedef 条目、无泛型模板;builtins.vp 已不再声明它,
|
||||||
|
# 见 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME 与 TPV 同口径内置分支
|
||||||
|
# TypeGenResolve.vp:921)。落到下方 typedef 表判据必然 miss ⇒ 误报
|
||||||
|
# "Undefined function: 'rawptr'"(TransPyV/App/lib/core/Gen/
|
||||||
|
# StructGenMixin.vp `vt_ptr: rawptr = rawptr(*(uint64*)(default_val))` 根因)。
|
||||||
|
# 单独 rawptr 等价 t.CPtr(void* → i8*):目标类型取内置 rawptr 的权威
|
||||||
|
# CTypeInfo(_builtin_rawptr_info,与注解侧同源),转换复用 (rawptr[X])(x)
|
||||||
|
# / (t.CPtr)(x) 的 _emit_rawptr_cast——不新造第二套 rawptr 表示。
|
||||||
|
# 不照抄 uchar 的做法(往 builtins.vp 加 `uchar: t.CTypedef = ...`):
|
||||||
|
# rawptr 不是普通 typedef 名而是内置语法,TPV register_ctypedef 显式拒绝
|
||||||
|
# 重定义(TypeGenCType.vp:157-160)、TPC builtins_typedef_value 亦硬编码
|
||||||
|
# 返回内置值(TypeAnnotationResolver.py:313-314)⇒ 该声明不生效。
|
||||||
|
# 本分支置于下方 try 之外:解析/转换异常必须外抛,不得被吞成
|
||||||
|
# "Undefined function: 'rawptr'" 的误导报文。
|
||||||
|
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_RP
|
||||||
|
if FuncName == _TAR_RP._BUILTIN_ROWPTR_NAME:
|
||||||
|
_rawptr_target: ir.Type = Gen._ctype_to_llvm(_TAR_RP._builtin_rawptr_info())
|
||||||
|
return self._emit_rawptr_cast(Node, Gen, _rawptr_target)
|
||||||
# builtins 隐式 CTypedef 作为类型转换调用(uint64(x)/int64(x)/size_t(x) 等,
|
# builtins 隐式 CTypedef 作为类型转换调用(uint64(x)/int64(x)/size_t(x) 等,
|
||||||
# builtins.vp 声明、无需 import,不注册进 SymbolTable):常规 miss 后在此
|
# builtins.vp 声明、无需 import,不注册进 SymbolTable):常规 miss 后在此
|
||||||
# 尝试 typedef cast(uint64(buf) = t.CUInt64T(buf)),否则报
|
# 尝试 typedef cast(uint64(buf) = t.CUInt64T(buf)),否则报
|
||||||
# "Undefined function: 'uint64'"(includes/ast/base.vp bootstrap 根因)。
|
# "Undefined function: 'uint64'"(includes/ast/base.vp bootstrap 根因)。
|
||||||
|
_td_diag: str = ''
|
||||||
try:
|
try:
|
||||||
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_TD
|
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_TD
|
||||||
if FuncName in _TAR_TD._scan_builtins()['typedefs']:
|
_td_table: dict = _TAR_TD._scan_builtins()['typedefs']
|
||||||
|
if FuncName not in _td_table:
|
||||||
|
# 典型成因:includes/builtins.vp 未定位到(PROJECT_INCLUDE_DIRS 尚未
|
||||||
|
# 装配)、或声明写法未被文本扫描识别 ⇒ 整表为空/缺项。
|
||||||
|
_td_diag = (f"(builtins typedef 表 {len(_td_table)} 项,无 '{FuncName}';"
|
||||||
|
f"includes 目录={_TAR_TD._builtins_include_dirs()})")
|
||||||
|
if FuncName in _td_table:
|
||||||
# from_type_name 对裸 typedef 名(uint64 等)解析不出真实类型类
|
# from_type_name 对裸 typedef 名(uint64 等)解析不出真实类型类
|
||||||
# (BuiltinTypeMap 无此键,CreateFromTypeName 落空成空 CType),
|
# (BuiltinTypeMap 无此键,CreateFromTypeName 落空成空 CType),
|
||||||
# 因此改走权威 typedef 值:'t.CUInt64T' → CTypeRegistry.NameToLLVM
|
# 因此改走权威 typedef 值:'t.CUInt64T' → CTypeRegistry.NameToLLVM
|
||||||
@@ -487,9 +696,11 @@ class ExprCallHandle(BaseHandle):
|
|||||||
_tg_ptr_td: ir.Type = ir.PointerType(_tg_td)
|
_tg_ptr_td: ir.Type = ir.PointerType(_tg_td)
|
||||||
return self._emit_rawptr_cast(Node, Gen, _tg_ptr_td)
|
return self._emit_rawptr_cast(Node, Gen, _tg_ptr_td)
|
||||||
return self._emit_rawptr_cast(Node, Gen, _tg_td)
|
return self._emit_rawptr_cast(Node, Gen, _tg_td)
|
||||||
except Exception:
|
except Exception as _e_td:
|
||||||
pass
|
# 不再静默:兜底链任何一步抛错都要带出来(此前 pass 让真正的根因
|
||||||
raise Exception(f"Undefined function: '{FuncName}'")
|
# 只剩一句误导性的 "Undefined function")。
|
||||||
|
_td_diag = _td_diag + f"(typedef cast 求值异常: {type(_e_td).__name__}: {_e_td})"
|
||||||
|
raise Exception(f"Undefined function: '{FuncName}'{_td_diag}")
|
||||||
if isinstance(Node.func, ast.Subscript):
|
if isinstance(Node.func, ast.Subscript):
|
||||||
# Handle list[int](mb) syntax — generic class constructor with explicit type args
|
# Handle list[int](mb) syntax — generic class constructor with explicit type args
|
||||||
# Handle xxx[T](x) syntax — generic function call with explicit type args
|
# Handle xxx[T](x) syntax — generic function call with explicit type args
|
||||||
@@ -558,39 +769,12 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
|
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
|
||||||
pointee = ObjVal.type.pointee
|
pointee = ObjVal.type.pointee
|
||||||
if isinstance(pointee, ir.ArrayType):
|
if isinstance(pointee, ir.ArrayType):
|
||||||
# Calculate element size in bytes
|
# 元素尺寸统一走 Gen._get_struct_size(含字段对齐/填充,与 __sizeof__ /
|
||||||
elem = pointee.element
|
# memhub alloc 同源):_get_struct_size(ArrayType) = 元素尺寸 × 元素数。
|
||||||
if isinstance(elem, ir.IntType):
|
# 原实现手写「字段宽度直接相加 + 未知类型兜底 8」,会漏算 struct 的填充
|
||||||
elem_size = elem.width // 8
|
# ({i8,i64} 算成 9 而非 16),sizeof 偏小 → 调用方按过小尺寸 alloc →
|
||||||
elif isinstance(elem, ir.PointerType):
|
# 越界写坏邻居块头(与 1 GiB 主池漏满同一根因链)。
|
||||||
elem_size = 8 # 64-bit pointers
|
return ir.Constant(ir.IntType(64), Gen._get_struct_size(pointee))
|
||||||
elif isinstance(elem, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
|
||||||
# Approximate: sum of field sizes
|
|
||||||
elem_size = 0
|
|
||||||
for field in getattr(elem, 'elements', []):
|
|
||||||
if isinstance(field, ir.IntType):
|
|
||||||
elem_size += field.width // 8
|
|
||||||
elif isinstance(field, ir.PointerType):
|
|
||||||
elem_size += 8
|
|
||||||
elif isinstance(field, ir.ArrayType):
|
|
||||||
if isinstance(field.element, ir.IntType):
|
|
||||||
elem_size += field.count * (field.element.width // 8)
|
|
||||||
else:
|
|
||||||
elem_size += field.count * 8
|
|
||||||
else:
|
|
||||||
elem_size += 8
|
|
||||||
elif isinstance(elem, ir.ArrayType):
|
|
||||||
if isinstance(elem.element, ir.IntType):
|
|
||||||
elem_size = elem.count * (elem.element.width // 8)
|
|
||||||
else:
|
|
||||||
elem_size = elem.count * 8
|
|
||||||
elif isinstance(elem, ir.FloatType):
|
|
||||||
elem_size = 4
|
|
||||||
elif isinstance(elem, ir.DoubleType):
|
|
||||||
elem_size = 8
|
|
||||||
else:
|
|
||||||
elem_size = 8 # fallback
|
|
||||||
return ir.Constant(ir.IntType(64), pointee.count * elem_size)
|
|
||||||
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||||||
# 结构体实例: 直接计算大小
|
# 结构体实例: 直接计算大小
|
||||||
self._resolve_opaque_struct_members(pointee, Gen)
|
self._resolve_opaque_struct_members(pointee, Gen)
|
||||||
@@ -649,6 +833,13 @@ class ExprCallHandle(BaseHandle):
|
|||||||
or _base_name in Gen.global_vars)
|
or _base_name in Gen.global_vars)
|
||||||
):
|
):
|
||||||
_is_chained_instance = True
|
_is_chained_instance = True
|
||||||
|
elif self._IsModuleQualifiedGlobalChain(_base_name, Node.func.value):
|
||||||
|
# 模块限定全局变量链:`Module.Global.field.method(...)`
|
||||||
|
# (如 GenApi._g_gen.ClassGenBase._collect_parents(...))。
|
||||||
|
# 链根是模块别名、次段是该模块的全局变量 ⇒ 接收者是对象,
|
||||||
|
# 与 self/局部变量/全局变量同一条"实例链"判据(详见
|
||||||
|
# _IsModuleQualifiedGlobalChain 的说明)。
|
||||||
|
_is_chained_instance = True
|
||||||
if (is_instance_var or _is_chained_instance) and not is_class_name:
|
if (is_instance_var or _is_chained_instance) and not is_class_name:
|
||||||
return self._HandleMethodCallLlvm(Node)
|
return self._HandleMethodCallLlvm(Node)
|
||||||
FuncAttr = Node.func.attr
|
FuncAttr = Node.func.attr
|
||||||
@@ -681,6 +872,10 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
|
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
|
||||||
msg = arg.value
|
msg = arg.value
|
||||||
lineno = getattr(Node, 'lineno', 0)
|
lineno = getattr(Node, 'lineno', 0)
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and lineno > _line_offset:
|
||||||
|
lineno -= _line_offset
|
||||||
raise Exception(f"#error: {msg} (line {lineno})")
|
raise Exception(f"#error: {msg} (line {lineno})")
|
||||||
elif FuncAttr in ('AsmInp', 'AsmOut'):
|
elif FuncAttr in ('AsmInp', 'AsmOut'):
|
||||||
return ir.Constant(ir.IntType(32), 0)
|
return ir.Constant(ir.IntType(32), 0)
|
||||||
@@ -1025,8 +1220,17 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if is_classmethod_call and ClassName in Gen.structs:
|
if is_classmethod_call and ClassName in Gen.structs:
|
||||||
ClsPtr = Gen._alloca(Gen.structs[ClassName], name=f"cls_{ClassName}")
|
ClsPtr = Gen._alloca(Gen.structs[ClassName], name=f"cls_{ClassName}")
|
||||||
CallArgs.append(ClsPtr)
|
CallArgs.append(ClsPtr)
|
||||||
for arg in Node.args:
|
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度
|
||||||
ArgVal = self.HandleExprLlvm(arg)
|
# (更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast);非常量实参走原路径。
|
||||||
|
_arg_off: int = len(CallArgs)
|
||||||
|
for _ai, arg in enumerate(Node.args):
|
||||||
|
_pt: ir.Type | None = None
|
||||||
|
if func is not None:
|
||||||
|
_ft_args: list[ir.Type] = func.function_type.args
|
||||||
|
if _ai + _arg_off < len(_ft_args):
|
||||||
|
_pt = _ft_args[_ai + _arg_off]
|
||||||
|
ArgVal = self.HandleExprLlvm(
|
||||||
|
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
|
||||||
if ArgVal:
|
if ArgVal:
|
||||||
CallArgs.append(ArgVal)
|
CallArgs.append(ArgVal)
|
||||||
if (ClassName not in Gen.class_vtable
|
if (ClassName not in Gen.class_vtable
|
||||||
@@ -1087,15 +1291,22 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if ClassName in Gen.structs:
|
if ClassName in Gen.structs:
|
||||||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
|
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
|
||||||
CallArgs = [ObjVal]
|
CallArgs = [ObjVal]
|
||||||
for arg in Node.args:
|
|
||||||
ArgVal = self.HandleExprLlvm(arg)
|
|
||||||
if ArgVal:
|
|
||||||
CallArgs.append(ArgVal)
|
|
||||||
CallFuncName = f'{ClassName}.__call__'
|
CallFuncName = f'{ClassName}.__call__'
|
||||||
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
|
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
|
||||||
class_sha1 = struct_sha1_map.get(ClassName)
|
class_sha1 = struct_sha1_map.get(ClassName)
|
||||||
if class_sha1:
|
if class_sha1:
|
||||||
CallFuncName = f'{class_sha1}.{ClassName}.__call__'
|
CallFuncName = f'{class_sha1}.{ClassName}.__call__'
|
||||||
|
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
|
||||||
|
# CallArgs[0] 为 self ⇒ 第 i 个用户实参对应形参 i+1。
|
||||||
|
_cfunc: ir.Function | None = (Gen._get_function(CallFuncName)
|
||||||
|
if Gen._has_function(CallFuncName) else None)
|
||||||
|
_ptypes: list[ir.Type] = _cfunc.function_type.args if _cfunc is not None else []
|
||||||
|
for _ai, arg in enumerate(Node.args):
|
||||||
|
_pt: ir.Type | None = _ptypes[_ai + 1] if _ai + 1 < len(_ptypes) else None
|
||||||
|
ArgVal = self.HandleExprLlvm(
|
||||||
|
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
|
||||||
|
if ArgVal:
|
||||||
|
CallArgs.append(ArgVal)
|
||||||
if not Gen._has_function(CallFuncName):
|
if not Gen._has_function(CallFuncName):
|
||||||
raise Exception(f"Class '{ClassName}' has no __call__ method")
|
raise Exception(f"Class '{ClassName}' has no __call__ method")
|
||||||
func = Gen._get_function(CallFuncName)
|
func = Gen._get_function(CallFuncName)
|
||||||
@@ -1131,6 +1342,66 @@ class ExprCallHandle(BaseHandle):
|
|||||||
parts.reverse()
|
parts.reverse()
|
||||||
return '.'.join(parts) if parts else None
|
return '.'.join(parts) if parts else None
|
||||||
|
|
||||||
|
def _IsModuleQualifiedGlobalChain(self, base_name: str, receiver: ast.AST) -> bool:
|
||||||
|
"""判定"模块限定全局变量"接收者链(链根=模块别名、次段=该模块全局变量)。
|
||||||
|
|
||||||
|
与调用点既有的"链根是已知变量(self / 局部变量 / 全局变量 / struct 变量)"
|
||||||
|
判据互补,补的是**模块限定全局变量**这一形态:
|
||||||
|
`import lib.core.Gen.GenApi as GenApi` 后写
|
||||||
|
`GenApi._g_gen.ClassGenBase._collect_parents(...)`——链根 `GenApi` 是模块
|
||||||
|
别名(不在 var_struct_class / variables / global_vars 中),次段 `_g_gen`
|
||||||
|
是该模块的全局变量。缺此判定时该接收者被当作"模块.类"引用,落到下方
|
||||||
|
`inner_attr in Gen.structs` 分支走 _HandleStaticMethodCallLlvm——该路径按
|
||||||
|
静态方法口径处理(**不前置接收者 self**),实参整体错位一格
|
||||||
|
(trans: Translator* 对上 max_parents: i32),触发 fail-fast
|
||||||
|
"指针类型 ... 不能隐式转换为整数参数 i32";不报错的同形调用(实参全为指针)
|
||||||
|
则静默生成缺 self 的错误调用。
|
||||||
|
|
||||||
|
判据只有两段:链根是模块(SymbolTable.import_aliases / IsModuleAlias 符号 /
|
||||||
|
ModuleSha1Map / _ImportedModules 任一命中),且**紧邻根名的那一段属性**不是
|
||||||
|
类型/函数类符号(类 / 枚举 / REnum / 枚举成员 / 函数 / CDefine / 异常类 /
|
||||||
|
模块别名)——即该段是"对象"(全局变量;跨模块全局在本符号表里登记为
|
||||||
|
IsTypedef 的空类型条目,故 IsTypedef 不在排除集内)。紧随段是类
|
||||||
|
(`Module.Class.static_method(...)`)时返回 False,静态方法口径保持不变。
|
||||||
|
"""
|
||||||
|
symbol_table: Any = self.Trans.SymbolTable
|
||||||
|
# 紧邻根名的那一段属性:GenApi._g_gen.ClassGenBase → '_g_gen'
|
||||||
|
first_attr: str | None = None
|
||||||
|
cursor: ast.AST = receiver
|
||||||
|
while isinstance(cursor, ast.Attribute):
|
||||||
|
first_attr = cursor.attr
|
||||||
|
cursor = cursor.value
|
||||||
|
if not first_attr or not isinstance(cursor, ast.Name):
|
||||||
|
return False
|
||||||
|
# 链根是不是模块:优先别名表(import ... as X 的 X → 模块全名),
|
||||||
|
# 其次符号表里的模块别名标记,最后模块表(其键含短名)兜底。
|
||||||
|
module_path: str = symbol_table.resolve_alias(base_name)
|
||||||
|
if module_path != base_name:
|
||||||
|
is_module: bool = True
|
||||||
|
else:
|
||||||
|
alias_entry: Any = symbol_table.lookup(base_name)
|
||||||
|
if alias_entry is not None and getattr(alias_entry, 'IsModuleAlias', False):
|
||||||
|
module_path = getattr(alias_entry, 'ResolvedModule', '') or ''
|
||||||
|
is_module = bool(module_path)
|
||||||
|
else:
|
||||||
|
is_module = (
|
||||||
|
base_name in getattr(self.Trans.LlvmGen, 'ModuleSha1Map', {})
|
||||||
|
or base_name in getattr(self.Trans, '_ImportedModules', set()))
|
||||||
|
if not is_module:
|
||||||
|
return False
|
||||||
|
segment: Any = symbol_table.lookup(f'{module_path}.{first_attr}') if module_path else None
|
||||||
|
if segment is None:
|
||||||
|
segment = symbol_table.lookup(first_attr)
|
||||||
|
if segment is None:
|
||||||
|
return False
|
||||||
|
# 该段是"类型/函数类"符号 ⇒ 属于 `Module.Class...` 引用,不按实例链处理
|
||||||
|
# (同名枚举成员/异常类在符号表里与类名冲突,一并排除,避免误判)。
|
||||||
|
for _type_flag in ('IsStruct', 'IsEnum', 'IsRenum', 'IsUnion', 'IsEnumMember',
|
||||||
|
'IsFunction', 'IsDefine', 'IsExceptionClass', 'IsModuleAlias'):
|
||||||
|
if getattr(segment, _type_flag, False):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def _HandleLLVMIRLlvm(self, Node: ast.Call) -> ir.Value:
|
def _HandleLLVMIRLlvm(self, Node: ast.Call) -> ir.Value:
|
||||||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||||||
if not Node.args:
|
if not Node.args:
|
||||||
@@ -1158,36 +1429,32 @@ class ExprCallHandle(BaseHandle):
|
|||||||
parts.append(value.value)
|
parts.append(value.value)
|
||||||
elif isinstance(value, ast.FormattedValue):
|
elif isinstance(value, ast.FormattedValue):
|
||||||
expr = value.value
|
expr = value.value
|
||||||
if isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute):
|
# c.LInp/c.LOut 与裸名 irinp/irout 等价(见 _intrinsic_marker)
|
||||||
if isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c':
|
_marker: str | None = self.Trans.ExprAsmHandle._intrinsic_marker(expr)
|
||||||
if expr.func.attr == 'LInp':
|
_is_c_qualified: bool = (isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute)
|
||||||
if expr.args:
|
and isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c')
|
||||||
val = self.HandleExprLlvm(expr.args[0])
|
if _marker == 'LInp':
|
||||||
if val:
|
if expr.args:
|
||||||
input_ops.append(val)
|
val = self.HandleExprLlvm(expr.args[0])
|
||||||
all_ops.append(('inp', len(input_ops) - 1))
|
if val:
|
||||||
parts.append(f'%__OP{operand_seq}__')
|
input_ops.append(val)
|
||||||
operand_seq += 1
|
|
||||||
elif expr.func.attr == 'LOut':
|
|
||||||
if expr.args:
|
|
||||||
target_ptr = self.Trans.ExprAsmHandle._get_var_ptr_for_asm(expr.args[0])
|
|
||||||
if target_ptr:
|
|
||||||
val = target_ptr
|
|
||||||
else:
|
|
||||||
val = self.HandleExprLlvm(expr.args[0])
|
|
||||||
if val:
|
|
||||||
output_targets.append(val)
|
|
||||||
all_ops.append(('out', len(output_targets) - 1))
|
|
||||||
parts.append(f'%__OP{operand_seq}__')
|
|
||||||
operand_seq += 1
|
|
||||||
else:
|
|
||||||
fallback = self.HandleExprLlvm(expr)
|
|
||||||
if fallback:
|
|
||||||
input_ops.append(fallback)
|
|
||||||
all_ops.append(('inp', len(input_ops) - 1))
|
all_ops.append(('inp', len(input_ops) - 1))
|
||||||
parts.append(f'%__OP{operand_seq}__')
|
parts.append(f'%__OP{operand_seq}__')
|
||||||
operand_seq += 1
|
operand_seq += 1
|
||||||
else:
|
elif _marker == 'LOut':
|
||||||
|
if expr.args:
|
||||||
|
target_ptr = self.Trans.ExprAsmHandle._get_var_ptr_for_asm(expr.args[0])
|
||||||
|
if target_ptr:
|
||||||
|
val = target_ptr
|
||||||
|
else:
|
||||||
|
val = self.HandleExprLlvm(expr.args[0])
|
||||||
|
if val:
|
||||||
|
output_targets.append(val)
|
||||||
|
all_ops.append(('out', len(output_targets) - 1))
|
||||||
|
parts.append(f'%__OP{operand_seq}__')
|
||||||
|
operand_seq += 1
|
||||||
|
elif not _is_c_qualified:
|
||||||
|
# c.* 中非 LInp/LOut 的形态不收集(保持原行为)
|
||||||
fallback = self.HandleExprLlvm(expr)
|
fallback = self.HandleExprLlvm(expr)
|
||||||
if fallback:
|
if fallback:
|
||||||
input_ops.append(fallback)
|
input_ops.append(fallback)
|
||||||
@@ -1551,6 +1818,14 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if sym_info_exact:
|
if sym_info_exact:
|
||||||
exact_params = sym_info_exact.FuncPtrParams
|
exact_params = sym_info_exact.FuncPtrParams
|
||||||
exact_param_names = [pn for pn, _ in exact_params]
|
exact_param_names = [pn for pn, _ in exact_params]
|
||||||
|
# stub/LLVM 侧形参名多为 arg0/arg1 占位(SymbolTable._InsertFuncSymbol),
|
||||||
|
# 用它们无法把命名实参 `f(cap = 8, x = 1)` 绑到正确位置,会误报"缺少必需
|
||||||
|
# 参数"。优先替换为源码/vpi 扫描得到的语义参数名(长度一致才替换,
|
||||||
|
# 避免 shape 不符时错配)。
|
||||||
|
semantic_names = (self._LookupGlobalParamNames(mangled_name)
|
||||||
|
or self._LookupGlobalParamNames(func_name))
|
||||||
|
if semantic_names and len(semantic_names) == len(exact_param_names):
|
||||||
|
exact_param_names = semantic_names
|
||||||
if exact_param_names:
|
if exact_param_names:
|
||||||
provided = len(Node.args)
|
provided = len(Node.args)
|
||||||
kw_provided = set()
|
kw_provided = set()
|
||||||
@@ -1558,17 +1833,9 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if kw.arg and kw.arg in exact_param_names:
|
if kw.arg and kw.arg in exact_param_names:
|
||||||
kw_provided.add(exact_param_names.index(kw.arg))
|
kw_provided.add(exact_param_names.index(kw.arg))
|
||||||
# 获取默认参数信息,计算必需参数数量(与 _fill_default_args 一致的查找逻辑)
|
# 获取默认参数信息,计算必需参数数量(与 _fill_default_args 一致的查找逻辑)
|
||||||
global_defaults: dict = self.Trans._global_function_default_args
|
func_defaults: list | None = self._LookupGlobalDefaults(mangled_name)
|
||||||
func_defaults: list | None = None
|
if func_defaults is None:
|
||||||
if mangled_name in global_defaults:
|
func_defaults = self._LookupGlobalDefaults(func_name)
|
||||||
func_defaults = global_defaults[mangled_name]
|
|
||||||
elif func_name in global_defaults:
|
|
||||||
func_defaults = global_defaults[func_name]
|
|
||||||
else:
|
|
||||||
for gk, gv in global_defaults.items():
|
|
||||||
if func_name in gk or gk.endswith(func_name) or gk.endswith('.' + func_name):
|
|
||||||
func_defaults = gv
|
|
||||||
break
|
|
||||||
total_params: int = len(exact_param_names)
|
total_params: int = len(exact_param_names)
|
||||||
if func_defaults is None:
|
if func_defaults is None:
|
||||||
# 默认参数信息缺失(跨模块 stub 加载:vpi/stub 只含参数
|
# 默认参数信息缺失(跨模块 stub 加载:vpi/stub 只含参数
|
||||||
@@ -1578,6 +1845,9 @@ class ExprCallHandle(BaseHandle):
|
|||||||
# err_type/col_start/col_end 默认值未同步时误报"缺 arg3")。
|
# err_type/col_start/col_end 默认值未同步时误报"缺 arg3")。
|
||||||
# 同文件函数的参数检查由 FunctionDef 路径(L1983)覆盖。
|
# 同文件函数的参数检查由 FunctionDef 路径(L1983)覆盖。
|
||||||
pass
|
pass
|
||||||
|
elif len(func_defaults) > total_params:
|
||||||
|
# 表里的条目与当前函数长度不符(同名不同模块)→ 不做严格检查
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
num_defaults: int = len(func_defaults)
|
num_defaults: int = len(func_defaults)
|
||||||
min_args: int = total_params - num_defaults
|
min_args: int = total_params - num_defaults
|
||||||
@@ -1645,6 +1915,10 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
|
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
|
||||||
msg = arg.value
|
msg = arg.value
|
||||||
lineno = getattr(Node, 'lineno', 0)
|
lineno = getattr(Node, 'lineno', 0)
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and lineno > _line_offset:
|
||||||
|
lineno -= _line_offset
|
||||||
raise Exception(f"#error: {msg} (line {lineno})")
|
raise Exception(f"#error: {msg} (line {lineno})")
|
||||||
elif func_name in ('AsmInp', 'AsmOut'):
|
elif func_name in ('AsmInp', 'AsmOut'):
|
||||||
return ir.Constant(ir.IntType(32), 0)
|
return ir.Constant(ir.IntType(32), 0)
|
||||||
@@ -2124,7 +2398,10 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if not is_variadic and len(Node.args) > regular_param_count:
|
if not is_variadic and len(Node.args) > regular_param_count:
|
||||||
raise Exception(f"调用 {FuncName}() 传入了 {len(Node.args)} 个参数,但函数只需要 {regular_param_count} 个参数")
|
raise Exception(f"调用 {FuncName}() 传入了 {len(Node.args)} 个参数,但函数只需要 {regular_param_count} 个参数")
|
||||||
|
|
||||||
max_args = len(Node.args) if is_variadic else regular_param_count
|
# 变参函数:固定形参(含带默认值的)之外才是可变部分。被省略的固定形参
|
||||||
|
# 也必须补进调用实参,否则实参个数少于固定形参 → 被调函数读到寄存器/栈
|
||||||
|
# 垃圾(VET json.write 缺省 cap 读成指针量级 → pool.alloc 巨量 → OOM 的形态)。
|
||||||
|
max_args = max(regular_param_count, len(Node.args)) if is_variadic else regular_param_count
|
||||||
resolved = [None] * max_args
|
resolved = [None] * max_args
|
||||||
|
|
||||||
for i, arg in enumerate(Node.args):
|
for i, arg in enumerate(Node.args):
|
||||||
@@ -2153,31 +2430,44 @@ class ExprCallHandle(BaseHandle):
|
|||||||
resolved[idx] = ArgVal
|
resolved[idx] = ArgVal
|
||||||
|
|
||||||
FuncDef_args_defaults = getattr(getattr(FuncDef, 'args', None), 'defaults', []) if FuncDef else []
|
FuncDef_args_defaults = getattr(getattr(FuncDef, 'args', None), 'defaults', []) if FuncDef else []
|
||||||
if FuncDef_args_defaults:
|
# 跨模块调用(被调函数不在本模块 FunctionDefCache:其它 App 模块 / includes
|
||||||
defaults = FuncDef.args.defaults
|
# 模块 / .vpi 声明)时 FuncDef 为 None,本模块没有被调方 AST,AST 默认值不可用。
|
||||||
num_defaults = len(defaults)
|
# 此时从全局默认参数表(Phase2 预扫描 App 与 includes 源码得到)取值,按
|
||||||
if num_defaults > 0:
|
# "尾部默认参数"语义补齐被省略的实参;缺失该表时保持原有零值填充。
|
||||||
min_args = regular_param_count - num_defaults
|
Global_defaults_vals: list | None = None
|
||||||
for i in range(regular_param_count):
|
if not FuncDef_args_defaults:
|
||||||
if resolved[i] is None and i >= min_args:
|
Global_defaults_vals = self._LookupGlobalDefaults(FuncName)
|
||||||
default_idx = i - min_args
|
if Global_defaults_vals is None and func is not None:
|
||||||
if default_idx < len(defaults):
|
Global_defaults_vals = self._LookupGlobalDefaults(getattr(func, 'name', None))
|
||||||
default_node = defaults[default_idx]
|
Num_defaults: int = len(FuncDef_args_defaults) if FuncDef_args_defaults else (
|
||||||
default_val = self.HandleExprLlvm(default_node)
|
len(Global_defaults_vals) if Global_defaults_vals else 0)
|
||||||
if default_val:
|
if Num_defaults > 0:
|
||||||
param_type = func.function_type.args[i]
|
Min_args: int = regular_param_count - Num_defaults
|
||||||
if default_val.type != param_type:
|
for i in range(regular_param_count):
|
||||||
if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType):
|
if resolved[i] is not None or i < Min_args:
|
||||||
if default_val.type.width < param_type.width:
|
continue
|
||||||
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}")
|
default_idx = i - Min_args
|
||||||
elif default_val.type.width > param_type.width:
|
if default_idx >= Num_defaults:
|
||||||
default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}")
|
continue
|
||||||
elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType):
|
if FuncDef_args_defaults:
|
||||||
default_val = Gen.builder.inttoptr(default_val, param_type, name=f"inttoptr_default_{i}")
|
default_val = self.HandleExprLlvm(FuncDef_args_defaults[default_idx])
|
||||||
resolved[i] = default_val
|
else:
|
||||||
|
default_val = self._ConstantToIrValue(Global_defaults_vals[default_idx],
|
||||||
|
func.function_type.args[i], Gen)
|
||||||
|
if default_val:
|
||||||
|
param_type = func.function_type.args[i]
|
||||||
|
if default_val.type != param_type:
|
||||||
|
if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType):
|
||||||
|
if default_val.type.width < param_type.width:
|
||||||
|
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}")
|
||||||
|
elif default_val.type.width > param_type.width:
|
||||||
|
default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}")
|
||||||
|
elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType):
|
||||||
|
default_val = Gen.builder.inttoptr(default_val, param_type, name=f"inttoptr_default_{i}")
|
||||||
|
resolved[i] = default_val
|
||||||
|
|
||||||
if FuncDef and param_names:
|
if FuncDef and param_names:
|
||||||
min_args = regular_param_count - num_defaults if FuncDef_args_defaults else regular_param_count
|
min_args = regular_param_count - Num_defaults
|
||||||
provided = len(Node.args)
|
provided = len(Node.args)
|
||||||
kw_provided = set()
|
kw_provided = set()
|
||||||
for kw in Node.keywords:
|
for kw in Node.keywords:
|
||||||
@@ -2361,7 +2651,7 @@ class ExprCallHandle(BaseHandle):
|
|||||||
return cmap
|
return cmap
|
||||||
with open(pyi_path, 'r', encoding='utf-8') as f:
|
with open(pyi_path, 'r', encoding='utf-8') as f:
|
||||||
pyi_code = f.read()
|
pyi_code = f.read()
|
||||||
tree = ast.parse(pyi_code)
|
tree = ast.parse(pyi_code, pyi_path)
|
||||||
cmap: dict[str, ast.ClassDef] = {}
|
cmap: dict[str, ast.ClassDef] = {}
|
||||||
for node in ast.iter_child_nodes(tree):
|
for node in ast.iter_child_nodes(tree):
|
||||||
if isinstance(node, ast.ClassDef):
|
if isinstance(node, ast.ClassDef):
|
||||||
@@ -2397,17 +2687,18 @@ class ExprCallHandle(BaseHandle):
|
|||||||
is_cvtable = self._has_cvtable_decorator(target_node)
|
is_cvtable = self._has_cvtable_decorator(target_node)
|
||||||
is_novtable = self._has_novtable_decorator(target_node)
|
is_novtable = self._has_novtable_decorator(target_node)
|
||||||
|
|
||||||
# 收集自身方法
|
# 收集自身方法(方法级修饰符的槽位过滤放在父类状态确定之后:
|
||||||
self_methods: list[str] = []
|
# 判据 SymbolUtils.IsVtableSlotMethod 需要 VtableByClassLevel)
|
||||||
|
self_method_pairs: list[tuple] = []
|
||||||
for item in target_node.body:
|
for item in target_node.body:
|
||||||
if isinstance(item, ast.FunctionDef):
|
if isinstance(item, ast.FunctionDef):
|
||||||
method_name = item.name
|
method_name = item.name
|
||||||
if method_name in ('__new__', '__init__', '__before_init__'):
|
if method_name in ('__new__', '__init__', '__before_init__'):
|
||||||
continue
|
continue
|
||||||
if method_name == '__call__':
|
if method_name == '__call__':
|
||||||
self_methods.append(f"{class_name}.__call__")
|
self_method_pairs.append((item, f"{class_name}.__call__"))
|
||||||
else:
|
else:
|
||||||
self_methods.append(f"{class_name}.{method_name}")
|
self_method_pairs.append((item, f"{class_name}.{method_name}"))
|
||||||
|
|
||||||
# 收集基类方法(多继承:遍历所有父类,声明顺序,短名去重,先声明父类优先)
|
# 收集基类方法(多继承:遍历所有父类,声明顺序,短名去重,先声明父类优先)
|
||||||
parent_methods: list[str] = []
|
parent_methods: list[str] = []
|
||||||
@@ -2466,6 +2757,24 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if first_parent_vtable:
|
if first_parent_vtable:
|
||||||
is_cvtable = True
|
is_cvtable = True
|
||||||
|
|
||||||
|
# VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态
|
||||||
|
# (= 类级 CVTable / 继承链判定)——供槽位筛选与"方法级触发"共用。
|
||||||
|
_VtByClassLevel: bool = is_cvtable
|
||||||
|
# 方法级修饰符筛选:`novirtual def`(⇒ @t.NoVTable)不进槽;
|
||||||
|
# `virtual def`(⇒ @t.CVTable)进槽。判据唯一源 = IsVtableSlotMethod
|
||||||
|
# (与定义端 HandlesClassDef / TPV _should_method_be_virtual 同口径),
|
||||||
|
# 故跨模块虚表槽序与定义模块逐槽一致。
|
||||||
|
self_methods: list[str] = []
|
||||||
|
for _nd, _full in self_method_pairs:
|
||||||
|
if (not is_cvtable) or IsVtableSlotMethod(_nd, _VtByClassLevel):
|
||||||
|
self_methods.append(_full)
|
||||||
|
# 方法级 virtual 触发类级 vtable(与 HandlesClassDef._resolve_class_flags /
|
||||||
|
# TPV _detect_vtable_status 的 `_has_any_func_cvtable` 分支对齐:
|
||||||
|
# 类级 NoVTable / 无真实父类这两种可判定形态;父类 vtable 已在上面处理)
|
||||||
|
if not is_cvtable and ClassHasFuncLevelCVTable(target_node):
|
||||||
|
if is_novtable or parent_name is None:
|
||||||
|
is_cvtable = True
|
||||||
|
|
||||||
# 合并方法列表:基类方法在前,子类方法在后
|
# 合并方法列表:基类方法在前,子类方法在后
|
||||||
# 子类覆盖的方法放在父类的位置;
|
# 子类覆盖的方法放在父类的位置;
|
||||||
# 父类独有方法直接用父类方法原名(实际定义类.方法,与 TPV 一致)
|
# 父类独有方法直接用父类方法原名(实际定义类.方法,与 TPV 一致)
|
||||||
@@ -2603,9 +2912,9 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None:
|
if st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None:
|
||||||
return None
|
return None
|
||||||
members: list = Gen.class_members.get(lookup_cls, [])
|
members: list = Gen.class_members.get(lookup_cls, [])
|
||||||
has_vtable: bool = (ClassName in Gen.class_vtable
|
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源),
|
||||||
or ClassName in Gen._cross_module_vtable_classes)
|
# 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。
|
||||||
base: int = 1 if has_vtable else 0
|
base: int = Gen._member_base(ClassName)
|
||||||
idx: int | None = None
|
idx: int | None = None
|
||||||
for i, (name, _) in enumerate(members):
|
for i, (name, _) in enumerate(members):
|
||||||
if name == field_name:
|
if name == field_name:
|
||||||
@@ -2628,7 +2937,7 @@ class ExprCallHandle(BaseHandle):
|
|||||||
try:
|
try:
|
||||||
with open(r'd:\Users\TermiNexus\Desktop\TransPyC\_diag_offset.log', 'a', encoding='utf-8') as _f:
|
with open(r'd:\Users\TermiNexus\Desktop\TransPyC\_diag_offset.log', 'a', encoding='utf-8') as _f:
|
||||||
_f.write(f"{lookup_cls}|{field_name}|members={len(members)}|struct={len(st.elements)}"
|
_f.write(f"{lookup_cls}|{field_name}|members={len(members)}|struct={len(st.elements)}"
|
||||||
f"|vtable={int(has_vtable)}|idx={idx}|diff={diff_elems}\n")
|
f"|base={base}|idx={idx}|diff={diff_elems}\n")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
offset: int = 0
|
offset: int = 0
|
||||||
@@ -2870,6 +3179,10 @@ class ExprCallHandle(BaseHandle):
|
|||||||
raise
|
raise
|
||||||
_vlog().warning(f"处理函数调用失败: {_e}", "Exception")
|
_vlog().warning(f"处理函数调用失败: {_e}", "Exception")
|
||||||
lineno = getattr(Node, 'lineno', 0)
|
lineno = getattr(Node, 'lineno', 0)
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and lineno > _line_offset:
|
||||||
|
lineno -= _line_offset
|
||||||
raise Exception(f"#error: {msg} (line {lineno})")
|
raise Exception(f"#error: {msg} (line {lineno})")
|
||||||
elif MethodName in ('CElse', 'CEndif', 'CPragma', 'CUndef'):
|
elif MethodName in ('CElse', 'CEndif', 'CPragma', 'CUndef'):
|
||||||
return ir.Constant(ir.IntType(1), 1)
|
return ir.Constant(ir.IntType(1), 1)
|
||||||
@@ -2904,12 +3217,16 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if (ModuleName in imported or actual_module in imported) and ModuleName not in Gen.var_struct_class:
|
if (ModuleName in imported or actual_module in imported) and ModuleName not in Gen.var_struct_class:
|
||||||
mangled_name = Gen._mangle_func_name(MethodName, actual_module)
|
mangled_name = Gen._mangle_func_name(MethodName, actual_module)
|
||||||
if Gen._has_function(mangled_name):
|
if Gen._has_function(mangled_name):
|
||||||
|
func = Gen._get_function(mangled_name)
|
||||||
CallArgs = []
|
CallArgs = []
|
||||||
for arg in Node.args:
|
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
|
||||||
ArgVal = self.HandleExprLlvm(arg)
|
_ptypes: list[ir.Type] = func.function_type.args if func is not None else []
|
||||||
|
for _ai, arg in enumerate(Node.args):
|
||||||
|
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
|
||||||
|
ArgVal = self.HandleExprLlvm(
|
||||||
|
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
|
||||||
if ArgVal:
|
if ArgVal:
|
||||||
CallArgs.append(ArgVal)
|
CallArgs.append(ArgVal)
|
||||||
func = Gen._get_function(mangled_name)
|
|
||||||
self._fill_default_args(CallArgs, func, MethodName)
|
self._fill_default_args(CallArgs, func, MethodName)
|
||||||
func_ft = getattr(func, 'function_type', None)
|
func_ft = getattr(func, 'function_type', None)
|
||||||
is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False
|
is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False
|
||||||
@@ -2961,8 +3278,12 @@ class ExprCallHandle(BaseHandle):
|
|||||||
_mf = Gen._get_function(MethodName)
|
_mf = Gen._get_function(MethodName)
|
||||||
if '.' not in _mf.name:
|
if '.' not in _mf.name:
|
||||||
CallArgs = []
|
CallArgs = []
|
||||||
for arg in Node.args:
|
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
|
||||||
ArgVal = self.HandleExprLlvm(arg)
|
_ptypes: list[ir.Type] = _mf.function_type.args
|
||||||
|
for _ai, arg in enumerate(Node.args):
|
||||||
|
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
|
||||||
|
ArgVal = self.HandleExprLlvm(
|
||||||
|
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
|
||||||
if ArgVal:
|
if ArgVal:
|
||||||
CallArgs.append(ArgVal)
|
CallArgs.append(ArgVal)
|
||||||
func = _mf
|
func = _mf
|
||||||
@@ -3026,8 +3347,12 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if stub_func_type:
|
if stub_func_type:
|
||||||
func = self.GetOrCreateStubFuncDecl(mangled_name, stub_func_type, Gen)
|
func = self.GetOrCreateStubFuncDecl(mangled_name, stub_func_type, Gen)
|
||||||
CallArgs = []
|
CallArgs = []
|
||||||
for arg in Node.args:
|
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
|
||||||
ArgVal = self.HandleExprLlvm(arg)
|
_ptypes: list[ir.Type] = func.function_type.args if func is not None else []
|
||||||
|
for _ai, arg in enumerate(Node.args):
|
||||||
|
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
|
||||||
|
ArgVal = self.HandleExprLlvm(
|
||||||
|
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
|
||||||
if ArgVal:
|
if ArgVal:
|
||||||
CallArgs.append(ArgVal)
|
CallArgs.append(ArgVal)
|
||||||
self._fill_default_args(CallArgs, func, mangled_name)
|
self._fill_default_args(CallArgs, func, mangled_name)
|
||||||
@@ -3419,8 +3744,8 @@ class ExprCallHandle(BaseHandle):
|
|||||||
member_idx = mi
|
member_idx = mi
|
||||||
break
|
break
|
||||||
if member_idx >= 0 and isinstance(ObjVal.type, ir.PointerType):
|
if member_idx >= 0 and isinstance(ObjVal.type, ir.PointerType):
|
||||||
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
|
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
|
||||||
base = 1 if has_vtable else 0
|
base = Gen._member_base(ClassName)
|
||||||
member_ptr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + member_idx)], name=f"funcptr_{MethodName}_ptr")
|
member_ptr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + member_idx)], name=f"funcptr_{MethodName}_ptr")
|
||||||
raw_ptr = Gen._load(member_ptr, name=f"funcptr_{MethodName}_raw")
|
raw_ptr = Gen._load(member_ptr, name=f"funcptr_{MethodName}_raw")
|
||||||
func_ptr_type = ir.PointerType(ir.FunctionType(ir.IntType(32), [i8ptr] + [a.type for a in CallArgs]))
|
func_ptr_type = ir.PointerType(ir.FunctionType(ir.IntType(32), [i8ptr] + [a.type for a in CallArgs]))
|
||||||
@@ -3845,9 +4170,19 @@ class ExprCallHandle(BaseHandle):
|
|||||||
# 所有元素存取同槽(get(0)==get(1),list[对象] join 误报根因)。
|
# 所有元素存取同槽(get(0)==get(1),list[对象] join 误报根因)。
|
||||||
if _BinOpContainsCPtr(Node):
|
if _BinOpContainsCPtr(Node):
|
||||||
return ir.Constant(ir.IntType(64), 8)
|
return ir.Constant(ir.IntType(64), 8)
|
||||||
return ir.Constant(ir.IntType(64), 0)
|
# 不带 t.CPtr 的联合(A | B)无法确定尺寸:曾静默返回 0 →
|
||||||
|
# 下游 elem_size=0 → alloc(0) 返回 NULL / 所有元素存取同槽。fail-fast。
|
||||||
|
raise ValueError(
|
||||||
|
f"[FATAL][__sizeof__] 联合类型 '{ast.unparse(Node)}' 不含 t.CPtr,"
|
||||||
|
f"无法确定尺寸,拒绝静默返回 0(会导致 alloc(0)/元素同槽)"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return ir.Constant(ir.IntType(64), 0)
|
# 非 名称/属性/联合 的 sizeof 目标(如表达式、调用):曾静默返回 0。fail-fast。
|
||||||
|
raise ValueError(
|
||||||
|
f"[FATAL][__sizeof__] 不支持的 sizeof 目标 {type(Node).__name__}"
|
||||||
|
f"({ast.unparse(Node)}),"
|
||||||
|
f"拒绝静默返回 0(会导致 alloc(0))"
|
||||||
|
)
|
||||||
|
|
||||||
if type_name not in Gen.structs:
|
if type_name not in Gen.structs:
|
||||||
# SHA1 前缀查找:如 ClassDef → 657e182b27c2a022.ClassDef
|
# SHA1 前缀查找:如 ClassDef → 657e182b27c2a022.ClassDef
|
||||||
@@ -3941,10 +4276,8 @@ class ExprCallHandle(BaseHandle):
|
|||||||
Gen.var_signedness = _saved_var_signedness
|
Gen.var_signedness = _saved_var_signedness
|
||||||
Gen.global_vars = _saved_global_vars
|
Gen.global_vars = _saved_global_vars
|
||||||
self.Trans.VarScopes = _saved_var_scopes
|
self.Trans.VarScopes = _saved_var_scopes
|
||||||
size = Gen._get_struct_size(struct_type)
|
# _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。
|
||||||
if size > 0:
|
return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type))
|
||||||
return ir.Constant(ir.IntType(64), size)
|
|
||||||
return ir.Constant(ir.IntType(64), 0)
|
|
||||||
|
|
||||||
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
|
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
|
||||||
Gen = self.Trans.LlvmGen
|
Gen = self.Trans.LlvmGen
|
||||||
@@ -4034,7 +4367,8 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if enum_member_found:
|
if enum_member_found:
|
||||||
enum_type_name = enum_class_name
|
enum_type_name = enum_class_name
|
||||||
if enum_type_name:
|
if enum_type_name:
|
||||||
type_str = f"<enum '{enum_type_name}' size=4 signed=true, ptr=false>"
|
_enum_bits: int = self.Trans.LlvmGen._enum_int_type(enum_type_name).width
|
||||||
|
type_str = f"<enum '{enum_type_name}' size={_enum_bits // 8} signed=true, ptr=false>"
|
||||||
else:
|
else:
|
||||||
llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg)
|
llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg)
|
||||||
if isinstance(llvm_type, ir.PointerType):
|
if isinstance(llvm_type, ir.PointerType):
|
||||||
@@ -4382,8 +4716,8 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if not find_func:
|
if not find_func:
|
||||||
return
|
return
|
||||||
members = Gen.class_members.get(ClassName, [])
|
members = Gen.class_members.get(ClassName, [])
|
||||||
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
|
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
|
||||||
base = 1 if has_vtable else 0
|
base = Gen._member_base(ClassName)
|
||||||
field_map: dict[str, tuple[int, ir.Type]] = {}
|
field_map: dict[str, tuple[int, ir.Type]] = {}
|
||||||
for i, (mname, mtype) in enumerate(members):
|
for i, (mname, mtype) in enumerate(members):
|
||||||
if mname in requires_all:
|
if mname in requires_all:
|
||||||
@@ -4447,10 +4781,9 @@ class ExprCallHandle(BaseHandle):
|
|||||||
NewMethodFunc = Gen._find_function(NewMethodFuncName)
|
NewMethodFunc = Gen._find_function(NewMethodFuncName)
|
||||||
if NewMethodFunc:
|
if NewMethodFunc:
|
||||||
NewMethodArgs = [result]
|
NewMethodArgs = [result]
|
||||||
for arg in Node.args:
|
for ArgIdx, arg in enumerate(Node.args, start=1):
|
||||||
ArgVal = self.HandleExprLlvm(arg)
|
ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node)
|
||||||
if ArgVal:
|
NewMethodArgs.append(ArgVal)
|
||||||
NewMethodArgs.append(ArgVal)
|
|
||||||
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
|
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
|
||||||
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
|
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
|
||||||
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
|
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
|
||||||
@@ -4479,10 +4812,9 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if Gen._has_function(InitFuncName):
|
if Gen._has_function(InitFuncName):
|
||||||
InitFunc = Gen._get_function(InitFuncName)
|
InitFunc = Gen._get_function(InitFuncName)
|
||||||
InitArgs = [result]
|
InitArgs = [result]
|
||||||
for arg in Node.args:
|
for ArgIdx, arg in enumerate(Node.args, start=1):
|
||||||
ArgVal = self.HandleExprLlvm(arg)
|
ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__init__", ArgIdx, arg, Node)
|
||||||
if ArgVal:
|
InitArgs.append(ArgVal)
|
||||||
InitArgs.append(ArgVal)
|
|
||||||
self._fill_default_args(InitArgs, InitFunc, InitFuncName)
|
self._fill_default_args(InitArgs, InitFunc, InitFuncName)
|
||||||
self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1)
|
self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1)
|
||||||
self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen)
|
self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen)
|
||||||
@@ -4522,10 +4854,9 @@ class ExprCallHandle(BaseHandle):
|
|||||||
NewMethodFunc = Gen._find_function(NewMethodFuncName)
|
NewMethodFunc = Gen._find_function(NewMethodFuncName)
|
||||||
if NewMethodFunc:
|
if NewMethodFunc:
|
||||||
NewMethodArgs = [result]
|
NewMethodArgs = [result]
|
||||||
for arg in Node.args:
|
for ArgIdx, arg in enumerate(Node.args, start=1):
|
||||||
ArgVal = self.HandleExprLlvm(arg)
|
ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node)
|
||||||
if ArgVal:
|
NewMethodArgs.append(ArgVal)
|
||||||
NewMethodArgs.append(ArgVal)
|
|
||||||
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
|
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
|
||||||
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
|
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
|
||||||
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
|
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
|
||||||
@@ -4563,9 +4894,8 @@ class ExprCallHandle(BaseHandle):
|
|||||||
init_param_types = InitFunc.function_type.args if hasattr(InitFunc, 'function_type') else []
|
init_param_types = InitFunc.function_type.args if hasattr(InitFunc, 'function_type') else []
|
||||||
for i, arg in enumerate(Node.args):
|
for i, arg in enumerate(Node.args):
|
||||||
arg_var_type = init_param_types[i + 1] if i + 1 < len(init_param_types) else None
|
arg_var_type = init_param_types[i + 1] if i + 1 < len(init_param_types) else None
|
||||||
ArgVal = self.HandleExprLlvm(arg, VarType=arg_var_type)
|
ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg, VarType=arg_var_type), f"{ClassName}.__init__", i + 1, arg, Node)
|
||||||
if ArgVal:
|
InitArgs.append(ArgVal)
|
||||||
InitArgs.append(ArgVal)
|
|
||||||
self._fill_default_args(InitArgs, InitFunc, InitFuncName)
|
self._fill_default_args(InitArgs, InitFunc, InitFuncName)
|
||||||
self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1)
|
self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1)
|
||||||
self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen)
|
self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen)
|
||||||
@@ -4613,8 +4943,8 @@ class ExprCallHandle(BaseHandle):
|
|||||||
return
|
return
|
||||||
if not isinstance(StructType, ir.IdentifiedStructType):
|
if not isinstance(StructType, ir.IdentifiedStructType):
|
||||||
return
|
return
|
||||||
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
|
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
|
||||||
base = 1 if has_vtable else 0
|
base = Gen._member_base(ClassName)
|
||||||
for i, (member_name, member_type) in enumerate(members):
|
for i, (member_name, member_type) in enumerate(members):
|
||||||
if member_name in defaults:
|
if member_name in defaults:
|
||||||
default_val = defaults[member_name]
|
default_val = defaults[member_name]
|
||||||
@@ -4627,8 +4957,8 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if not isinstance(StructType, ir.IdentifiedStructType):
|
if not isinstance(StructType, ir.IdentifiedStructType):
|
||||||
return
|
return
|
||||||
members = Gen.class_members.get(ClassName, [])
|
members = Gen.class_members.get(ClassName, [])
|
||||||
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
|
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
|
||||||
base = 1 if has_vtable else 0
|
base = Gen._member_base(ClassName)
|
||||||
for i, arg in enumerate(Node.args):
|
for i, arg in enumerate(Node.args):
|
||||||
if i >= len(members):
|
if i >= len(members):
|
||||||
break
|
break
|
||||||
@@ -4674,6 +5004,9 @@ class ExprCallHandle(BaseHandle):
|
|||||||
|
|
||||||
def _HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None:
|
def _HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None:
|
||||||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||||||
|
# pointee-volatile 基址名后缀(同 HandlesExprAttr._HandleSubscriptLlvm);空后缀
|
||||||
|
# ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。
|
||||||
|
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
|
||||||
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen)
|
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen)
|
||||||
if ClassName and ClassName in Gen.structs and Gen._has_function(f'{ClassName}.__getitem__'):
|
if ClassName and ClassName in Gen.structs and Gen._has_function(f'{ClassName}.__getitem__'):
|
||||||
obj_val = self.HandleExprLlvm(Node.value)
|
obj_val = self.HandleExprLlvm(Node.value)
|
||||||
@@ -4706,7 +5039,7 @@ class ExprCallHandle(BaseHandle):
|
|||||||
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
|
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
|
||||||
LoadedValueVal = Gen._load(ValueVal, name="Load_subscript")
|
LoadedValueVal = Gen._load(ValueVal, name="Load_subscript")
|
||||||
if isinstance(LoadedValueVal.type, ir.PointerType):
|
if isinstance(LoadedValueVal.type, ir.PointerType):
|
||||||
ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
if isinstance(ValueVal.type, ir.PointerType):
|
if isinstance(ValueVal.type, ir.PointerType):
|
||||||
pointee = ValueVal.type.pointee
|
pointee = ValueVal.type.pointee
|
||||||
@@ -4717,12 +5050,12 @@ class ExprCallHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
return Gen._load(ElemPtr, name="arr_subscript_val")
|
return Gen._load(ElemPtr, name="arr_subscript_val")
|
||||||
else:
|
else:
|
||||||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
|
||||||
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
if isinstance(pointee, ir.IntType):
|
if isinstance(pointee, ir.IntType):
|
||||||
return Gen._load(ElemPtr, name="subscript_val")
|
return Gen._load(ElemPtr, name="subscript_val")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
@@ -4738,11 +5071,11 @@ class ExprCallHandle(BaseHandle):
|
|||||||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||||||
else:
|
else:
|
||||||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||||||
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
arr_alloc = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
arr_alloc = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
||||||
Gen._store(ValueVal, arr_alloc)
|
Gen._store(ValueVal, arr_alloc)
|
||||||
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript")
|
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
|
||||||
return ElemPtr
|
return ElemPtr
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -194,8 +194,18 @@ class ExprLambdaHandle(BaseHandle):
|
|||||||
result_type = ir.IntType(8)
|
result_type = ir.IntType(8)
|
||||||
# 重新处理 Else 分支的类型转换
|
# 重新处理 Else 分支的类型转换
|
||||||
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
|
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
|
||||||
# 指针转整数
|
if isinstance(BodyVal.type.pointee, ir.IntType) and BodyVal.type.pointee.width == 8:
|
||||||
BodyVal = Gen.builder.ptrtoint(BodyVal, result_type, name="ifexp_body_ptr2int")
|
# 1 字符字符串字面量(i8*)与 char(i8)混用(如
|
||||||
|
# `p if p is not None else '?'`):取首字符。与 else 侧对称
|
||||||
|
# (旧代码仅 else 侧这样处理,body 侧 ptrtoint 截断地址是错的)
|
||||||
|
BodyVal = Gen._load(BodyVal, name="ifexp_body_Load_char")
|
||||||
|
else:
|
||||||
|
# 其余指针 → 整数:隐式转换一律禁止(两分支类型不一致须显式 cast)
|
||||||
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"指针隐式转整数已禁止:条件表达式两分支类型不一致({BodyVal.type} / {result_type});"
|
||||||
|
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
|
)
|
||||||
Gen.builder.branch(MergeBB)
|
Gen.builder.branch(MergeBB)
|
||||||
# 在 ElseBB 末尾添加类型转换(如果需要)和分支
|
# 在 ElseBB 末尾添加类型转换(如果需要)和分支
|
||||||
if not ElseEndBB.is_terminated:
|
if not ElseEndBB.is_terminated:
|
||||||
@@ -213,11 +223,17 @@ class ExprLambdaHandle(BaseHandle):
|
|||||||
result_type = ir.IntType(8)
|
result_type = ir.IntType(8)
|
||||||
# 重新处理 BodyVal 分支的类型转换(已经在上面处理过了)
|
# 重新处理 BodyVal 分支的类型转换(已经在上面处理过了)
|
||||||
elif isinstance(OrelseVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
|
elif isinstance(OrelseVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
|
||||||
# 如果指针指向 i8,加载字符值
|
|
||||||
if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8:
|
if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8:
|
||||||
|
# 1 字符字符串字面量(i8*)与 char(i8)混用(原语义,保留):
|
||||||
|
# 取首字符;只有非 i8* 的指针转整数才 fail-fast
|
||||||
OrelseVal = Gen._load(OrelseVal, name="ifexp_orelse_Load_char")
|
OrelseVal = Gen._load(OrelseVal, name="ifexp_orelse_Load_char")
|
||||||
else:
|
else:
|
||||||
OrelseVal = Gen.builder.ptrtoint(OrelseVal, result_type, name="ifexp_orelse_ptr2int")
|
# 其余指针 → 整数:隐式转换一律禁止(两分支类型不一致须显式 cast)
|
||||||
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"指针隐式转整数已禁止:条件表达式两分支类型不一致({OrelseVal.type} / {result_type});"
|
||||||
|
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
|
)
|
||||||
Gen.builder.branch(MergeBB)
|
Gen.builder.branch(MergeBB)
|
||||||
Gen.builder.position_at_start(MergeBB)
|
Gen.builder.position_at_start(MergeBB)
|
||||||
result_phi: Any = Gen.builder.phi(result_type, name="ifexp.result")
|
result_phi: Any = Gen.builder.phi(result_type, name="ifexp.result")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ if TYPE_CHECKING:
|
|||||||
from lib.core.Handles.HandlesBase import BaseHandle
|
from lib.core.Handles.HandlesBase import BaseHandle
|
||||||
import vast as ast
|
import vast as ast
|
||||||
import llvmlite.ir as ir
|
import llvmlite.ir as ir
|
||||||
|
from lib.core.SymbolUtils import RequireIntLiteralStorable
|
||||||
|
|
||||||
|
|
||||||
class ExprOpsHandle(BaseHandle):
|
class ExprOpsHandle(BaseHandle):
|
||||||
@@ -108,6 +109,10 @@ class ExprOpsHandle(BaseHandle):
|
|||||||
vname: str = Node.left.id
|
vname: str = Node.left.id
|
||||||
if vname in Gen.var_signedness and Gen.var_signedness[vname]:
|
if vname in Gen.var_signedness and Gen.var_signedness[vname]:
|
||||||
is_unsigned = True
|
is_unsigned = True
|
||||||
|
# 双指针算术 fail-fast 已**下沉到单一收敛点** `Gen.emit_binary_op`
|
||||||
|
# (LLVMCG/ExprGen.py):BinOp 与 AugAssign(+=、-=、*= …)都经该函数,
|
||||||
|
# 判据(运算符属算术/位运算/移位集合 且 两操作数都是 LLVM 指针)与报文
|
||||||
|
# 在那里一次收口。此处不再保留副本,避免两处判据/报文分叉。
|
||||||
result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned)
|
result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -244,6 +249,12 @@ class ExprOpsHandle(BaseHandle):
|
|||||||
elif OpSymbol == '+':
|
elif OpSymbol == '+':
|
||||||
return OperandVal
|
return OperandVal
|
||||||
elif OpSymbol == '-':
|
elif OpSymbol == '-':
|
||||||
|
# 一元减 + 整数字面量的**联合越界判定**(与 TPV `_parse_factor` 同口径):
|
||||||
|
# `-` 不属于字面量,幅值合法(≤ 2^64-1)的字面量取负后可能 < -2^63。
|
||||||
|
if isinstance(Node.operand, ast.Constant) \
|
||||||
|
and isinstance(Node.operand.value, int) \
|
||||||
|
and not isinstance(Node.operand.value, bool):
|
||||||
|
RequireIntLiteralStorable(Gen, self.Trans, Node, -Node.operand.value)
|
||||||
if isinstance(OperandVal.type, ir.IntType):
|
if isinstance(OperandVal.type, ir.IntType):
|
||||||
return Gen.builder.neg(OperandVal, name="neg_result")
|
return Gen.builder.neg(OperandVal, name="neg_result")
|
||||||
elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)):
|
elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)):
|
||||||
|
|||||||
@@ -339,7 +339,9 @@ class ExprUtils:
|
|||||||
attr_name: str = node.attr
|
attr_name: str = node.attr
|
||||||
AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
|
AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
|
||||||
if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None):
|
if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None):
|
||||||
return ir.IntType(32)
|
# 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型)
|
||||||
|
return self.Trans.LlvmGen._enum_int_type(
|
||||||
|
getattr(AttrInfo, 'EnumName', None), AttrInfo)
|
||||||
return ir.IntType(32)
|
return ir.IntType(32)
|
||||||
elif isinstance(node, ast.BinOp):
|
elif isinstance(node, ast.BinOp):
|
||||||
left_type: ir.Type = self._infer_expr_llvm_type_full(node.left)
|
left_type: ir.Type = self._infer_expr_llvm_type_full(node.left)
|
||||||
|
|||||||
@@ -360,16 +360,10 @@ class ForHandle(BaseHandle):
|
|||||||
old_var: ir.Value = Gen.variables[var_name]
|
old_var: ir.Value = Gen.variables[var_name]
|
||||||
if isinstance(old_var.type, ir.PointerType):
|
if isinstance(old_var.type, ir.PointerType):
|
||||||
elem_type: ir.Type = old_var.type.pointee
|
elem_type: ir.Type = old_var.type.pointee
|
||||||
# 计算元素大小(字节)
|
# 尺寸统一走 Gen._get_struct_size(与 __sizeof__ / alloc 同源,含对齐/填充):
|
||||||
elem_size: int = 8 # 默认指针大小
|
# 原实现只认 Int/Float/Double/Pointer,struct/array 落到「默认 8」,
|
||||||
if isinstance(elem_type, ir.IntType):
|
# 于是 malloc(8) 后整块 store → 堆越界写坏相邻块。
|
||||||
elem_size = max(1, elem_type.width // 8)
|
elem_size: int = Gen._get_struct_size(elem_type)
|
||||||
elif isinstance(elem_type, ir.FloatType):
|
|
||||||
elem_size = 4
|
|
||||||
elif isinstance(elem_type, ir.DoubleType):
|
|
||||||
elem_size = 8
|
|
||||||
elif isinstance(elem_type, ir.PointerType):
|
|
||||||
elem_size = 8
|
|
||||||
# malloc 堆存储
|
# malloc 堆存储
|
||||||
heap_raw: ir.Value = Gen.builder.call(malloc_func, [ir.Constant(ir.IntType(64), elem_size)], name=f"heap_{var_name}")
|
heap_raw: ir.Value = Gen.builder.call(malloc_func, [ir.Constant(ir.IntType(64), elem_size)], name=f"heap_{var_name}")
|
||||||
heap_ptr: ir.Value = Gen.builder.bitcast(heap_raw, ir.PointerType(elem_type), name=f"heap_{var_name}_ptr")
|
heap_ptr: ir.Value = Gen.builder.bitcast(heap_raw, ir.PointerType(elem_type), name=f"heap_{var_name}_ptr")
|
||||||
|
|||||||
@@ -898,7 +898,7 @@ class FunctionHandle(BaseHandle):
|
|||||||
if '[' in replacement:
|
if '[' in replacement:
|
||||||
SubNode: ast.AST | None = None
|
SubNode: ast.AST | None = None
|
||||||
try:
|
try:
|
||||||
SubNode = ast.parse(replacement, mode='eval').body
|
SubNode = ast.parse(replacement, '<stub:type_expr>', mode='eval').body
|
||||||
except Exception:
|
except Exception:
|
||||||
SubNode = None
|
SubNode = None
|
||||||
if not isinstance(SubNode, ast.Subscript):
|
if not isinstance(SubNode, ast.Subscript):
|
||||||
@@ -936,7 +936,7 @@ class FunctionHandle(BaseHandle):
|
|||||||
slice_node = self._make_replacement_node(inner_name)
|
slice_node = self._make_replacement_node(inner_name)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
slice_node = ast.parse(inner_name, mode='eval').body
|
slice_node = ast.parse(inner_name, '<stub:type_expr>', mode='eval').body
|
||||||
except Exception:
|
except Exception:
|
||||||
slice_node = self._make_name_node(inner_name)
|
slice_node = self._make_name_node(inner_name)
|
||||||
if slice_node is None:
|
if slice_node is None:
|
||||||
@@ -1627,8 +1627,10 @@ class FunctionHandle(BaseHandle):
|
|||||||
if Arg.annotation:
|
if Arg.annotation:
|
||||||
try:
|
try:
|
||||||
PInfo = self.ResolveAnnotationType(Arg.annotation)
|
PInfo = self.ResolveAnnotationType(Arg.annotation)
|
||||||
if PInfo and (PInfo.DataConst or PInfo.VarConst):
|
# const/volatile 形参登记(统一登记入口)。const 判据沿用形参历史
|
||||||
Gen.var_const_flags[Arg.arg] = True
|
# 条件(IsParam=True,见 BaseGen.RegisterVarQualifiers 说明);
|
||||||
|
# volatile / pointee-volatile 与 TransPyV 侧同口径。
|
||||||
|
Gen.RegisterVarQualifiers(Arg.arg, PInfo, Arg.annotation, IsParam=True)
|
||||||
except Exception as _e:
|
except Exception as _e:
|
||||||
if _config_mode == "strict":
|
if _config_mode == "strict":
|
||||||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||||||
@@ -2140,6 +2142,10 @@ class FunctionHandle(BaseHandle):
|
|||||||
Gen.var_struct_class = {}
|
Gen.var_struct_class = {}
|
||||||
Gen.var_ptr_element = {}
|
Gen.var_ptr_element = {}
|
||||||
Gen.var_const_flags = {}
|
Gen.var_const_flags = {}
|
||||||
|
# const/volatile 的**函数作用域层**随函数出栈清空(模块级层 global_* 与
|
||||||
|
# 类字段层 field_* 是持久的,不在此清;见 BaseGen 的登记说明)。
|
||||||
|
Gen.var_volatile_flags = {}
|
||||||
|
Gen.var_pointee_volatile_flags = {}
|
||||||
# 恢复保存的 var_type_info
|
# 恢复保存的 var_type_info
|
||||||
Gen.var_type_info = saved_var_type_info
|
Gen.var_type_info = saved_var_type_info
|
||||||
Gen.var_type_assignments = saved_var_type_assignments
|
Gen.var_type_assignments = saved_var_type_assignments
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ from lib.constants.config import mode as _config_mode
|
|||||||
from lib.includes import t
|
from lib.includes import t
|
||||||
from lib.includes.t import CTypeRegistry
|
from lib.includes.t import CTypeRegistry
|
||||||
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext
|
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext
|
||||||
|
from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords
|
||||||
|
from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod
|
||||||
|
|
||||||
|
|
||||||
# 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写,
|
# 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写,
|
||||||
@@ -25,6 +27,107 @@ _MARKER_ANNOT_NAMES: frozenset[str] = frozenset({
|
|||||||
'State', 'TLS',
|
'State', 'TLS',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# 标记基类名(不是真实父类):与 HandlesClassDef._EmitClassLlvm 的 base 跳过表同口径。
|
||||||
|
_MARKER_BASE_NAMES: frozenset[str] = frozenset({
|
||||||
|
'Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum',
|
||||||
|
'Exception', 'Enum',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
class ImportHandle(BaseHandle):
|
class ImportHandle(BaseHandle):
|
||||||
_struct_Load_cache = {}
|
_struct_Load_cache = {}
|
||||||
@@ -353,10 +456,21 @@ class ImportHandle(BaseHandle):
|
|||||||
try:
|
try:
|
||||||
with open(pyi_path, 'r', encoding='utf-8') as f:
|
with open(pyi_path, 'r', encoding='utf-8') as f:
|
||||||
ModuleCode = f.read()
|
ModuleCode = f.read()
|
||||||
ModuleTree = ast.parse(ModuleCode)
|
# 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:
|
except Exception as _e:
|
||||||
if _config_mode == "strict":
|
# 统一诊断口径(strict fail-fast / 非严格告警),必带文件+行号+异常+后果。
|
||||||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
# 原实现"吞异常 + return"(strict 也只写进无人消费的 Trans.Warnings),
|
||||||
|
# 是本次 `Undefined function: '_init_ast'` 被伪装成下游症状的直接原因。
|
||||||
|
_ReportDeclarationParseFailure(
|
||||||
|
pyi_path, _e,
|
||||||
|
"该模块的全部声明(函数/类/全局/枚举)不会注册,"
|
||||||
|
"后续引用方可能表现为 Undefined function / Undefined struct")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 计算源模块名(从 .pyi 文件路径推导)。
|
# 计算源模块名(从 .pyi 文件路径推导)。
|
||||||
@@ -715,7 +829,9 @@ class ImportHandle(BaseHandle):
|
|||||||
try:
|
try:
|
||||||
with open(FullModulePath, 'r', encoding='utf-8') as f:
|
with open(FullModulePath, 'r', encoding='utf-8') as f:
|
||||||
ModuleCode = f.read()
|
ModuleCode = f.read()
|
||||||
ModuleTree = ast.parse(ModuleCode)
|
# 同上:`.vp` 源码须先过 Viper 软关键字层再 vast.parse(否则含
|
||||||
|
# `enum X:` / `fn` 的模块声明整体丢失,落到 except 的 stub 兜底)。
|
||||||
|
ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), FullModulePath)
|
||||||
if module_name == 'pyzlib':
|
if module_name == 'pyzlib':
|
||||||
func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)]
|
func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)]
|
||||||
has_functions_or_classes = False
|
has_functions_or_classes = False
|
||||||
@@ -854,6 +970,21 @@ class ImportHandle(BaseHandle):
|
|||||||
elif 'includes' in FullModulePath and module_name:
|
elif 'includes' in FullModulePath and module_name:
|
||||||
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
|
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
|
||||||
except Exception as e:
|
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:
|
if module_name:
|
||||||
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
|
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
|
||||||
|
|
||||||
@@ -1597,6 +1728,13 @@ class ImportHandle(BaseHandle):
|
|||||||
if isinstance(arg.value, 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':
|
if getattr(arg.value.value, 'id', None) == 't' and arg.value.attr == 'attr' and arg.attr == 'packed':
|
||||||
IsPacked = True
|
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,
|
return (IsGenericClass, IsCenum, IsRenum, IsCpythonObject,
|
||||||
IsCVTable, IsNoVTable, IsPacked)
|
IsCVTable, IsNoVTable, IsPacked)
|
||||||
|
|
||||||
@@ -1642,6 +1780,14 @@ class ImportHandle(BaseHandle):
|
|||||||
EnumTypeNode.Name = ClassName
|
EnumTypeNode.Name = ClassName
|
||||||
EnumTypeNode.BaseType = t.CEnum(ClassName)
|
EnumTypeNode.BaseType = t.CEnum(ClassName)
|
||||||
EnumTypeNode.IsEnum = True
|
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)
|
self.Trans.SymbolTable.insert(ClassName, EnumTypeNode)
|
||||||
next_enum_value: int = 0
|
next_enum_value: int = 0
|
||||||
for item in Node.body:
|
for item in Node.body:
|
||||||
@@ -1672,6 +1818,7 @@ class ImportHandle(BaseHandle):
|
|||||||
MemberNode.EnumName = ClassName
|
MemberNode.EnumName = ClassName
|
||||||
MemberNode.Lineno = item.lineno
|
MemberNode.Lineno = item.lineno
|
||||||
MemberNode.IsEnumMember = True
|
MemberNode.IsEnumMember = True
|
||||||
|
MemberNode.set('enum_width', EnumWidth)
|
||||||
self.Trans.SymbolTable.insert(VarName, MemberNode)
|
self.Trans.SymbolTable.insert(VarName, MemberNode)
|
||||||
self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode)
|
self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode)
|
||||||
self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode)
|
self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode)
|
||||||
@@ -1776,6 +1923,26 @@ class ImportHandle(BaseHandle):
|
|||||||
next_tag = value + 1
|
next_tag = value + 1
|
||||||
RenumTypeNode.RenumVariants = variant_names
|
RenumTypeNode.RenumVariants = variant_names
|
||||||
return
|
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
|
# 先初始化 class_methods/class_members 等容器,确保 _TryLoadStructFromStub
|
||||||
# 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods)
|
# 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods)
|
||||||
# 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32)
|
# 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32)
|
||||||
@@ -1814,6 +1981,11 @@ class ImportHandle(BaseHandle):
|
|||||||
# 会被解析为 opaque struct,LLVM 报 "invalid type for function argument"。
|
# 会被解析为 opaque struct,LLVM 报 "invalid type for function argument"。
|
||||||
# 字段(AnnAssign)仍正常加载,确保继承字段(如 GSListNode.Next)可被展平。
|
# 字段(AnnAssign)仍正常加载,确保继承字段(如 GSListNode.Next)可被展平。
|
||||||
if not IsGenericClass:
|
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:
|
for _item in Node.body:
|
||||||
if isinstance(_item, ast.FunctionDef):
|
if isinstance(_item, ast.FunctionDef):
|
||||||
_has_methods_prescan = True
|
_has_methods_prescan = True
|
||||||
@@ -1825,7 +1997,8 @@ class ImportHandle(BaseHandle):
|
|||||||
if _MethodName in ('__new__', '__init__', '__before_init__'):
|
if _MethodName in ('__new__', '__init__', '__before_init__'):
|
||||||
continue
|
continue
|
||||||
_FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}"
|
_FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}"
|
||||||
if _FuncFullName not in Gen.class_methods[ClassName]:
|
_IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(_item, _VtByClassLevel)
|
||||||
|
if _IsSlot and _FuncFullName not in Gen.class_methods[ClassName]:
|
||||||
Gen.class_methods[ClassName].append(_FuncFullName)
|
Gen.class_methods[ClassName].append(_FuncFullName)
|
||||||
if _has_methods_prescan and IsCVTable:
|
if _has_methods_prescan and IsCVTable:
|
||||||
Gen._cross_module_vtable_classes.add(ClassName)
|
Gen._cross_module_vtable_classes.add(ClassName)
|
||||||
@@ -2016,10 +2189,14 @@ class ImportHandle(BaseHandle):
|
|||||||
# 构造函数(__new__/__init__/__before_init__)不放入 vtable,
|
# 构造函数(__new__/__init__/__before_init__)不放入 vtable,
|
||||||
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。
|
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。
|
||||||
# register_method 内部也会过滤,此处仅跳过 class_methods.append。
|
# 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 MethodName not in ('__new__', '__init__', '__before_init__'):
|
||||||
if FuncFullName not in Gen.class_methods[ClassName]:
|
if _IsSlot2 and FuncFullName not in Gen.class_methods[ClassName]:
|
||||||
Gen.class_methods[ClassName].append(FuncFullName)
|
Gen.class_methods[ClassName].append(FuncFullName)
|
||||||
Gen.register_method(ClassName, 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_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_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_classmethod = any(isinstance(d, ast.Name) and d.id == 'classmethod' for d in item.decorator_list)
|
||||||
@@ -2158,6 +2335,13 @@ class ImportHandle(BaseHandle):
|
|||||||
# 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。
|
# 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。
|
||||||
_any_parent_loaded: bool = False
|
_any_parent_loaded: bool = False
|
||||||
_all_inherited: list[tuple] = []
|
_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 集合(必须在 for 循环外,跨所有父类
|
||||||
# 共享——否则每个父类新建 seen,菱形祖先(如 ClassGenBase)被每个
|
# 共享——否则每个父类新建 seen,菱形祖先(如 ClassGenBase)被每个
|
||||||
# 继承它的 ClassGen* 父类各展平一次 → 重复(Generator 曾 707 字段)
|
# 继承它的 ClassGen* 父类各展平一次 → 重复(Generator 曾 707 字段)
|
||||||
@@ -2270,6 +2454,14 @@ class ImportHandle(BaseHandle):
|
|||||||
_inherited_ph: list[tuple] = [(f'__inherited_{i}', _st.elements[i]) for i in range(_num_inherited)]
|
_inherited_ph: list[tuple] = [(f'__inherited_{i}', _st.elements[i]) for i in range(_num_inherited)]
|
||||||
# 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致
|
# 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致
|
||||||
Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName]
|
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。
|
# 成员收集 + 继承展平完成 → 解除收集中保护,并用完整 class_members 设置 body。
|
||||||
# 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的
|
# 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的
|
||||||
# _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque
|
# _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque
|
||||||
@@ -2277,6 +2469,10 @@ class ImportHandle(BaseHandle):
|
|||||||
# 本保护要消除的目标。
|
# 本保护要消除的目标。
|
||||||
Gen._collecting_members_set.discard(ClassName)
|
Gen._collecting_members_set.discard(ClassName)
|
||||||
Gen._generate_structs()
|
Gen._generate_structs()
|
||||||
|
if _flat_complete:
|
||||||
|
# 登记"该表已按父类展平",供 _TryLoadClassMembersFromPyi 的 guard 与
|
||||||
|
# _member_base 的查询前补齐使用。未登记 ⇒ 后续任一查询都可重跑本节补齐。
|
||||||
|
Gen._members_flattened.add(ClassName)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位),
|
# 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位),
|
||||||
# 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只
|
# 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只
|
||||||
@@ -2301,12 +2497,18 @@ class ImportHandle(BaseHandle):
|
|||||||
for _m in Gen.class_methods.get(ClassName, []):
|
for _m in Gen.class_methods.get(ClassName, []):
|
||||||
_mn: str = _m.split('.')[-1] if '.' in _m else _m
|
_mn: str = _m.split('.')[-1] if '.' in _m else _m
|
||||||
_child_method_map[_mn] = _m
|
_child_method_map[_mn] = _m
|
||||||
# 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册)
|
# 从 .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', []):
|
for _item_m in getattr(Node, 'body', []):
|
||||||
if isinstance(_item_m, ast.FunctionDef):
|
if isinstance(_item_m, ast.FunctionDef):
|
||||||
_mn2: str = _item_m.name
|
_mn2: str = _item_m.name
|
||||||
if _mn2 in ('__new__', '__init__', '__before_init__'):
|
if _mn2 in ('__new__', '__init__', '__before_init__'):
|
||||||
continue
|
continue
|
||||||
|
if not IsVtableSlotMethod(_item_m, _VtByClassLevelC):
|
||||||
|
continue
|
||||||
_child_method_map[_mn2] = f"{ClassName}.{_mn2}"
|
_child_method_map[_mn2] = f"{ClassName}.{_mn2}"
|
||||||
_new_methods: list[str] = []
|
_new_methods: list[str] = []
|
||||||
_covered_names: set[str] = set()
|
_covered_names: set[str] = set()
|
||||||
@@ -2348,6 +2550,8 @@ class ImportHandle(BaseHandle):
|
|||||||
bw = bitfields.get(name, 0)
|
bw = bitfields.get(name, 0)
|
||||||
Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset
|
Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset
|
||||||
current_bit_offset += bw
|
current_bit_offset += bw
|
||||||
|
# 声明发射结束:解除重入标记(与上方 add 配对)。
|
||||||
|
_emit_inprog.discard(_emit_key)
|
||||||
|
|
||||||
def _build_stub_index(self) -> None:
|
def _build_stub_index(self) -> None:
|
||||||
"""扫描所有 .stub.ll 文件构建 class_name → 条目列表索引
|
"""扫描所有 .stub.ll 文件构建 class_name → 条目列表索引
|
||||||
@@ -2408,6 +2612,28 @@ class ImportHandle(BaseHandle):
|
|||||||
_vlog().warning(f"构建 stub 索引失败: {_e}", "Exception")
|
_vlog().warning(f"构建 stub 索引失败: {_e}", "Exception")
|
||||||
self._stub_index_scanned.add(stub_path)
|
self._stub_index_scanned.add(stub_path)
|
||||||
|
|
||||||
|
@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:
|
def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None) -> None:
|
||||||
# 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义,
|
# 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义,
|
||||||
# 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致
|
# 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致
|
||||||
@@ -2503,7 +2729,17 @@ class ImportHandle(BaseHandle):
|
|||||||
raise
|
raise
|
||||||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||||||
stub_filename = actual_sha1 + '.stub.ll'
|
stub_filename = actual_sha1 + '.stub.ll'
|
||||||
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
|
# 【递归环闭合边 · 重入短路】该类的声明发射尚未结束(`_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' 占位条目、
|
# 根本修复:加载后 struct 仍为 opaque 时(如选中 'type opaque' 占位条目、
|
||||||
# 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久
|
# 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久
|
||||||
# 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。
|
# 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。
|
||||||
@@ -2515,6 +2751,82 @@ class ImportHandle(BaseHandle):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
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 _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin,
|
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin,
|
||||||
force: bool = False) -> bool:
|
force: bool = False) -> bool:
|
||||||
"""从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。
|
"""从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。
|
||||||
@@ -2526,19 +2838,42 @@ class ImportHandle(BaseHandle):
|
|||||||
"""
|
"""
|
||||||
ProjectRoot = self._find_project_root()
|
ProjectRoot = self._find_project_root()
|
||||||
temp_dir = os.path.join(ProjectRoot, 'temp')
|
temp_dir = os.path.join(ProjectRoot, 'temp')
|
||||||
if (not force) and class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0:
|
# guard(2026-xx 收紧):只在"表非空 **且已登记为已展平**"时短路。原实现
|
||||||
# guard 命中:class_members 已存在(可能从别处预置、不完整),但
|
# 只看 `len>0`,于是"停在自有字段"的残缺表(展平未完成)一旦生成就再也不能
|
||||||
# class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names
|
# 补齐(顺序敏感)——这正是自有字段下标缺继承前缀的成因。未展平的表允许
|
||||||
|
# 重跑 `_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)
|
# 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114)
|
||||||
# → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载
|
# → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载
|
||||||
# class_members,避免覆盖可能更完整的已有数据)。
|
# class_members,避免覆盖可能更完整的已有数据)。
|
||||||
|
_path0: str = os.path.join(temp_dir, stub_filename.replace('.stub.ll', '.vpi'))
|
||||||
try:
|
try:
|
||||||
_pyi_b0: str = stub_filename.replace('.stub.ll', '.vpi')
|
|
||||||
_path0: str = os.path.join(temp_dir, _pyi_b0)
|
|
||||||
_tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0)
|
_tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0)
|
||||||
if _tree0 is None and os.path.isfile(_path0):
|
if _tree0 is None and os.path.isfile(_path0):
|
||||||
with open(_path0, 'r', encoding='utf-8') as _fh0:
|
with open(_path0, 'r', encoding='utf-8') as _fh0:
|
||||||
_tree0 = ast.parse(_fh0.read())
|
_tree0 = ast.parse(_fh0.read(), _path0)
|
||||||
getattr(self, '_pyi_cache', {})[_path0] = _tree0
|
getattr(self, '_pyi_cache', {})[_path0] = _tree0
|
||||||
if _tree0 is not None:
|
if _tree0 is not None:
|
||||||
for _n0 in _tree0.body:
|
for _n0 in _tree0.body:
|
||||||
@@ -2553,8 +2888,14 @@ class ImportHandle(BaseHandle):
|
|||||||
Gen.class_parents = {}
|
Gen.class_parents = {}
|
||||||
Gen.class_parents[class_name] = _b0
|
Gen.class_parents[class_name] = _b0
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception as _e0:
|
||||||
pass
|
# 同族口径:此处原为 `except Exception: pass`(静默)。class_parents
|
||||||
|
# 未回填 ⇒ 多继承/菱形展平不完整 ⇒ class_members 长度不确定 ⇒
|
||||||
|
# this_adjust 错位。改为显式诊断(strict fail-fast / 非严格告警)。
|
||||||
|
_ReportDeclarationParseFailure(
|
||||||
|
_path0, _e0,
|
||||||
|
f"类 '{class_name}' 的 class_parents 未能回填,"
|
||||||
|
"多继承展平可能不完整(字段下标/this_adjust 可能错位)")
|
||||||
return False
|
return False
|
||||||
pyi_basename = stub_filename.replace('.stub.ll', '.vpi')
|
pyi_basename = stub_filename.replace('.stub.ll', '.vpi')
|
||||||
pyi_path = os.path.join(temp_dir, pyi_basename)
|
pyi_path = os.path.join(temp_dir, pyi_basename)
|
||||||
@@ -2576,9 +2917,15 @@ class ImportHandle(BaseHandle):
|
|||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
with open(pyi_path, 'r', encoding='utf-8') as f:
|
with open(pyi_path, 'r', encoding='utf-8') as f:
|
||||||
self._pyi_cache[pyi_path] = ast.parse(f.read())
|
self._pyi_cache[pyi_path] = ast.parse(f.read(), pyi_path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
# 同族口径:原告警不带文件名("忽略异常"),改为显式诊断
|
||||||
|
# (strict fail-fast / 非严格告警)。.vpi 是编译器无糖产物,
|
||||||
|
# 解析失败属内部不变量被破坏,非严格模式仍缓存 None 并返回 False。
|
||||||
|
_ReportDeclarationParseFailure(
|
||||||
|
pyi_path, e,
|
||||||
|
f"类 '{class_name}' 的成员/父类信息不会加载,"
|
||||||
|
"class_members 可能残缺(字段下标/this_adjust 可能错位)")
|
||||||
self._pyi_cache[pyi_path] = None
|
self._pyi_cache[pyi_path] = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -2621,6 +2968,8 @@ class ImportHandle(BaseHandle):
|
|||||||
# 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。
|
# 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。
|
||||||
# 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。
|
# 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。
|
||||||
Gen.class_members[class_name] = []
|
Gen.class_members[class_name] = []
|
||||||
|
# 同步撤销"已展平"登记:重建若因依赖未就绪而不完整,须允许后续查询再补齐。
|
||||||
|
Gen._members_flattened.discard(class_name)
|
||||||
for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields',
|
for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields',
|
||||||
'class_member_byteorders', 'class_member_bitoffsets'):
|
'class_member_byteorders', 'class_member_bitoffsets'):
|
||||||
_gd: dict | None = getattr(Gen, _gd_attr, None)
|
_gd: dict | None = getattr(Gen, _gd_attr, None)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from lib.core.translator import Translator
|
from lib.core.translator import Translator
|
||||||
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
|
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
|
||||||
|
from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType
|
||||||
import vast as ast
|
import vast as ast
|
||||||
import llvmlite.ir as ir
|
import llvmlite.ir as ir
|
||||||
|
|
||||||
@@ -96,7 +97,11 @@ class ReturnHandle(BaseHandle):
|
|||||||
IsRetPtr = True
|
IsRetPtr = True
|
||||||
ExpectedType: ir.Type = ReturnTypeInfo.ToLLVM(Gen) if ReturnTypeInfo and ReturnTypeInfo.BaseType else Gen._CType2LLVM('int', IsRetPtr)
|
ExpectedType: ir.Type = ReturnTypeInfo.ToLLVM(Gen) if ReturnTypeInfo and ReturnTypeInfo.BaseType else Gen._CType2LLVM('int', IsRetPtr)
|
||||||
expected_types.append(ExpectedType)
|
expected_types.append(ExpectedType)
|
||||||
Val: ir.Value | None = self.HandleExprLlvm(val)
|
# 返回注解即"使用处宽度":CDefine 常量在此按返回注解的位宽物化
|
||||||
|
# (更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast);非常量返回值不受影响
|
||||||
|
# (ConstWidthHint 只被常量物化分支读取)。
|
||||||
|
Val: ir.Value | None = self.HandleExprLlvm(
|
||||||
|
val, ConstWidthHint=DefineUseWidthFromLLVMType(ExpectedType))
|
||||||
if Val is None:
|
if Val is None:
|
||||||
if isinstance(ExpectedType, ir.PointerType):
|
if isinstance(ExpectedType, ir.PointerType):
|
||||||
Val = ir.Constant(ExpectedType, None)
|
Val = ir.Constant(ExpectedType, None)
|
||||||
@@ -131,9 +136,12 @@ class ReturnHandle(BaseHandle):
|
|||||||
Gen.emit_return(result)
|
Gen.emit_return(result)
|
||||||
return
|
return
|
||||||
if Node.value:
|
if Node.value:
|
||||||
Val: ir.Value | None = self.HandleExprLlvm(Node.value)
|
# 本函数返回类型即"使用处宽度":先取回返回类型再翻译值,供 CDefine 常量
|
||||||
|
# 按该位宽物化(更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast)。
|
||||||
|
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
|
||||||
|
Val: ir.Value | None = self.HandleExprLlvm(
|
||||||
|
Node.value, ConstWidthHint=DefineUseWidthFromLLVMType(ret_type))
|
||||||
if Val:
|
if Val:
|
||||||
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
|
|
||||||
if isinstance(ret_type, ir.IdentifiedStructType) and isinstance(Val, ir.Constant) and isinstance(Val.type, ir.PointerType) and Val.constant is None:
|
if isinstance(ret_type, ir.IdentifiedStructType) and isinstance(Val, ir.Constant) and isinstance(Val.type, ir.PointerType) and Val.constant is None:
|
||||||
if ret_type.elements:
|
if ret_type.elements:
|
||||||
zero_val: ir.Constant = ir.Constant(ret_type, [
|
zero_val: ir.Constant = ir.Constant(ret_type, [
|
||||||
|
|||||||
@@ -5,37 +5,13 @@ import llvmlite.ir as ir
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from lib.core.translator import Translator
|
from lib.core.translator import Translator
|
||||||
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
|
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
|
||||||
from lib.core.Handles.HandlesBase import BaseHandle, BuiltinTypeMap
|
from lib.core.Handles.HandlesBase import BaseHandle
|
||||||
from lib.includes.t import CTypeRegistry
|
from lib.includes.t import CTypeRegistry
|
||||||
from lib.core.SymbolUtils import IsTModule
|
from lib.core.SymbolUtils import IsTModule
|
||||||
from lib.core.VLogger import get_logger as _vlog
|
from lib.core.VLogger import get_logger as _vlog
|
||||||
from lib.constants.config import mode as _config_mode
|
from lib.constants.config import mode as _config_mode
|
||||||
|
|
||||||
|
|
||||||
def _IsTypeExpr(node: ast.AST | None) -> bool:
|
|
||||||
"""判断 AST 节点是否为类型表达式(类型转换的目标,如 Name/Attribute/Subscript/BinOp|)"""
|
|
||||||
return (
|
|
||||||
isinstance(node, ast.Name)
|
|
||||||
or isinstance(node, ast.Attribute)
|
|
||||||
or isinstance(node, ast.Subscript)
|
|
||||||
or (isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _IsCastWrappingDeref(rhs: ast.AST | None, deref_node: ast.Call) -> bool:
|
|
||||||
"""判断 rhs 是否为"类型转换链包裹 c.Deref"(如 (T | t.CPtr)(t.CPtr(c.Deref(p))))。
|
|
||||||
|
|
||||||
允许在 c.Deref 处于类型转换调用链中时仍从赋值注解推断加载类型;
|
|
||||||
ifexp/compare/binop 包裹(非转换调用)不放行,避免把 i8* 错 bitcast 成
|
|
||||||
其他位宽加载(见 _infer_deref_target_type 文档)。
|
|
||||||
"""
|
|
||||||
if rhs is deref_node:
|
|
||||||
return True
|
|
||||||
if isinstance(rhs, ast.Call) and len(rhs.args) == 1 and _IsTypeExpr(rhs.func):
|
|
||||||
return _IsCastWrappingDeref(rhs.args[0], deref_node)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class CSpecialCallHandle(BaseHandle):
|
class CSpecialCallHandle(BaseHandle):
|
||||||
def _HandleCIfLlvm(self, Node: ast.Call) -> ir.Value:
|
def _HandleCIfLlvm(self, Node: ast.Call) -> ir.Value:
|
||||||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||||||
@@ -251,7 +227,6 @@ class CSpecialCallHandle(BaseHandle):
|
|||||||
if arg_val is None:
|
if arg_val is None:
|
||||||
return ir.Constant(ir.IntType(32), 0)
|
return ir.Constant(ir.IntType(32), 0)
|
||||||
if isinstance(arg_val.type, ir.PointerType):
|
if isinstance(arg_val.type, ir.PointerType):
|
||||||
pointee: ir.Type = arg_val.type.pointee
|
|
||||||
# 嵌套 Deref 的内层:加载为指针 (i8*),不推断目标类型
|
# 嵌套 Deref 的内层:加载为指针 (i8*),不推断目标类型
|
||||||
# 因为结果会被外层 Deref 再次解引用
|
# 因为结果会被外层 Deref 再次解引用
|
||||||
NestedLoadPtr: bool = getattr(Gen, '_nested_deref_load_ptr', False)
|
NestedLoadPtr: bool = getattr(Gen, '_nested_deref_load_ptr', False)
|
||||||
@@ -261,75 +236,17 @@ class CSpecialCallHandle(BaseHandle):
|
|||||||
Loaded = Gen._load(casted, name="deref")
|
Loaded = Gen._load(casted, name="deref")
|
||||||
if Loaded is not None:
|
if Loaded is not None:
|
||||||
return Loaded
|
return Loaded
|
||||||
# void* (i8*) 需要根据赋值目标类型推断加载类型
|
# 读取宽度只由指针的 pointee 类型决定(_load 按 arg_val.type.pointee
|
||||||
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
|
# 加载一个元素):pointee 是 i8(t.CPtr / void* / str / bytes)即恒读
|
||||||
target_type: ir.Type | None = self._infer_deref_target_type(Gen, Node)
|
# 1 字节,不再按赋值目标注解反推宽度——那会让同一处 c.Deref(i8*) 的
|
||||||
if target_type is not None and not isinstance(target_type, ir.IntType):
|
# 读取宽度取决于它是否恰好出现在带注解赋值的 RHS 里。需要 4/8 字节等
|
||||||
# bitcast i8* -> target_type* then load
|
# 更宽读取时,由调用点显式给出元素类型指针后再解引用
|
||||||
casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(target_type), name="deref_cast")
|
# (c.Deref((int | t.CPtr)(p)) / c.Deref(t.CUInt64T(p, t.CPtr)))。
|
||||||
Loaded = Gen._load(casted, name="deref")
|
|
||||||
if Loaded is not None:
|
|
||||||
return Loaded
|
|
||||||
elif target_type is not None and isinstance(target_type, ir.IntType):
|
|
||||||
# 目标是整数类型 (如 i32/i64): bitcast 并加载
|
|
||||||
if target_type.width != 8:
|
|
||||||
casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(target_type), name="deref_cast")
|
|
||||||
Loaded = Gen._load(casted, name="deref")
|
|
||||||
if Loaded is not None:
|
|
||||||
return Loaded
|
|
||||||
Loaded = Gen._load(arg_val, name="deref")
|
Loaded = Gen._load(arg_val, name="deref")
|
||||||
if Loaded is not None:
|
if Loaded is not None:
|
||||||
return Loaded
|
return Loaded
|
||||||
return arg_val
|
return arg_val
|
||||||
|
|
||||||
def _infer_deref_target_type(self, Gen: LlvmGeneratorMixin, DerefNode: ast.Call | None = None) -> ir.Type | None:
|
|
||||||
"""从当前赋值上下文推断 c.Deref 的目标加载类型。
|
|
||||||
当用户写 v: int = c.Deref(ptr) 时,从赋值注解推断目标类型。
|
|
||||||
注意:仅当 c.Deref 是赋值的直接 RHS,或 RHS 是类型转换链包裹
|
|
||||||
(如 `(T | t.CPtr)(t.CPtr(c.Deref(p)))`)时才推断;若 c.Deref 嵌套在
|
|
||||||
ifexp/compare/binop 中(如 `a = 1 if c.Deref(p) == 65 else 0`),
|
|
||||||
不应推断,否则会把 i8* 错误 bitcast 为 i32* 加载 4 字节。"""
|
|
||||||
assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None)
|
|
||||||
if not assign_node:
|
|
||||||
return None
|
|
||||||
# 仅当 c.Deref 是赋值的直接 RHS 或处于类型转换链中时才推断类型
|
|
||||||
if DerefNode is not None:
|
|
||||||
rhs: ast.AST | None = getattr(assign_node, 'value', None)
|
|
||||||
if rhs is not DerefNode and not _IsCastWrappingDeref(rhs, DerefNode):
|
|
||||||
return None
|
|
||||||
ann: ast.AST | None = getattr(assign_node, 'annotation', None)
|
|
||||||
if not ann:
|
|
||||||
return None
|
|
||||||
# 收集候选类型名:Name / Attribute / 联合 BinOp| 的所有 side。
|
|
||||||
# 逐个尝试解析,取第一个能解析为 LLVM 类型的(如
|
|
||||||
# `vthreading.Thread | t.CPtr` → 'Thread' 未注册 → 'CPtr' → i8*)。
|
|
||||||
candidates: list[str] = []
|
|
||||||
if isinstance(ann, ast.Name):
|
|
||||||
candidates.append(ann.id)
|
|
||||||
elif isinstance(ann, ast.Attribute):
|
|
||||||
candidates.append(ann.attr)
|
|
||||||
elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr):
|
|
||||||
# 处理 int | t.CPtr 形式的联合类型注解
|
|
||||||
for side in (ann.left, ann.right):
|
|
||||||
if isinstance(side, ast.Name):
|
|
||||||
candidates.append(side.id)
|
|
||||||
elif isinstance(side, ast.Attribute):
|
|
||||||
candidates.append(side.attr)
|
|
||||||
for ann_name in candidates:
|
|
||||||
# 使用 BuiltinTypeMap 统一查询(同时支持 Python 类名和字符串速记)
|
|
||||||
entry: tuple[type, int] | None = BuiltinTypeMap.Get(ann_name)
|
|
||||||
if entry is not None:
|
|
||||||
ctype_cls: type = entry[0]
|
|
||||||
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
|
|
||||||
if llvm_str:
|
|
||||||
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
|
|
||||||
if resolved:
|
|
||||||
return resolved
|
|
||||||
# 检查是否是已注册的结构体类型
|
|
||||||
if ann_name in Gen.structs:
|
|
||||||
return Gen.structs[ann_name]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _HandleCPtrToIntLlvm(self, Node: ast.Call) -> ir.Value:
|
def _HandleCPtrToIntLlvm(self, Node: ast.Call) -> ir.Value:
|
||||||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||||||
if not Node.args:
|
if not Node.args:
|
||||||
|
|||||||
@@ -141,8 +141,13 @@ class WithHandle(BaseHandle):
|
|||||||
current_depth: int = sum(len(pl) for pl in Gen._with_provides_stack)
|
current_depth: int = sum(len(pl) for pl in Gen._with_provides_stack)
|
||||||
new_fields: int = len(Gen.class_provides[ClassName])
|
new_fields: int = len(Gen.class_provides[ClassName])
|
||||||
if current_depth + new_fields > 32:
|
if current_depth + new_fields > 32:
|
||||||
|
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
|
||||||
|
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
|
||||||
|
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||||
|
if _line_offset > 0 and _lineno > _line_offset:
|
||||||
|
_lineno -= _line_offset
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"[TransPyC] with provider stack overflow at line {Node.lineno}: "
|
f"[TransPyC] with provider stack overflow at line {_lineno}: "
|
||||||
f"current depth {current_depth} + new {new_fields} fields > max 32. "
|
f"current depth {current_depth} + new {new_fields} fields > max 32. "
|
||||||
f"Class '{ClassName}' provides too many fields or with nesting too deep."
|
f"Class '{ClassName}' provides too many fields or with nesting too deep."
|
||||||
)
|
)
|
||||||
|
|||||||
+12
-12
@@ -257,10 +257,10 @@ class BaseGenMixin:
|
|||||||
# True=每个 define 发 DISubprogram + 入口首条指令发 DILocation。
|
# True=每个 define 发 DISubprogram + 入口首条指令发 DILocation。
|
||||||
self.dwarf_enabled: bool = False
|
self.dwarf_enabled: bool = False
|
||||||
self.DebugInfo: Any = None
|
self.DebugInfo: Any = None
|
||||||
# Ctrl+C 调用栈(--ctrlc-stack):仅在翻译 includes/dbgstack.vp 这一个模块时
|
# Ctrl+C 调用栈(--traceback):仅在翻译 includes/traceback.vp 这一个模块时
|
||||||
# 置 True —— 落成 .ll 文本前为本模块注入 @llvm.global_ctors 构造器调用其
|
# 置 True —— 落成 .ll 文本前为本模块注入 @llvm.global_ctors 构造器调用其
|
||||||
# Install()(见 _RegisterDbgStackInstallViaCtor)。默认 False(零影响)。
|
# Install()(见 _RegisterTracebackInstallViaCtor)。默认 False(零影响)。
|
||||||
self.ctrlc_stack_ctor: bool = False
|
self.traceback_ctor: bool = False
|
||||||
pi: dict[str, bool | int] = self._platform_info
|
pi: dict[str, bool | int] = self._platform_info
|
||||||
# 根据目标平台设置宏(声明式,从三元组推导)
|
# 根据目标平台设置宏(声明式,从三元组推导)
|
||||||
if pi['is_windows']:
|
if pi['is_windows']:
|
||||||
@@ -2100,12 +2100,12 @@ class BaseGenMixin:
|
|||||||
gv.linkage = 'appending'
|
gv.linkage = 'appending'
|
||||||
gv.initializer = ir.Constant(arr_ty, [entry_val])
|
gv.initializer = ir.Constant(arr_ty, [entry_val])
|
||||||
|
|
||||||
def _RegisterDbgStackInstallViaCtor(self) -> None:
|
def _RegisterTracebackInstallViaCtor(self) -> None:
|
||||||
"""用 @llvm.global_ctors 在进程启动期调用本模块的 Install()(--ctrlc-stack)。
|
"""用 @llvm.global_ctors 在进程启动期调用本模块的 Install()(--traceback)。
|
||||||
|
|
||||||
仅在"本翻译单元就是 includes/dbgstack.vp"时由 StructGen.finalize() 调用
|
仅在"本翻译单元就是 includes/traceback.vp"时由 StructGen.finalize() 调用
|
||||||
(ctrlc_stack_ctor 由 Phase2Translator._translate_file 按源文件名置位)。
|
(traceback_ctor 由 Phase2Translator._translate_file 按源文件名置位)。
|
||||||
生成 internal void @__dbgstack_install_ctor()(entry 块直接 call 本模块的
|
生成 internal void @__traceback_install_ctor()(entry 块直接 call 本模块的
|
||||||
Install() 并忽略其 int 返回值),再写入 appending linkage 的
|
Install() 并忽略其 int 返回值),再写入 appending linkage 的
|
||||||
@llvm.global_ctors —— 每模块一份,链接器按 appending 语义拼进 .init_array,
|
@llvm.global_ctors —— 每模块一份,链接器按 appending 语义拼进 .init_array,
|
||||||
进程启动时执行一次。
|
进程启动时执行一次。
|
||||||
@@ -2121,22 +2121,22 @@ class BaseGenMixin:
|
|||||||
install_fn: ir.Function | None = self._find_function('Install')
|
install_fn: ir.Function | None = self._find_function('Install')
|
||||||
if install_fn is None:
|
if install_fn is None:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'--ctrlc-stack: 在 dbgstack 模块内找不到 Install() 函数,'
|
'--traceback: 在 traceback 模块内找不到 Install() 函数,'
|
||||||
'无法注入启动期构造器(不静默降级)')
|
'无法注入启动期构造器(不静默降级)')
|
||||||
i32: ir.IntType = ir.IntType(32)
|
i32: ir.IntType = ir.IntType(32)
|
||||||
i8p: ir.PointerType = ir.PointerType(ir.IntType(8))
|
i8p: ir.PointerType = ir.PointerType(ir.IntType(8))
|
||||||
void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), []))
|
void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), []))
|
||||||
ctor: ir.Function = ir.Function(
|
ctor: ir.Function = ir.Function(
|
||||||
self.module, ir.FunctionType(ir.VoidType(), []),
|
self.module, ir.FunctionType(ir.VoidType(), []),
|
||||||
name=self._UniqueGlobalName('__dbgstack_install_ctor'))
|
name=self._UniqueGlobalName('__traceback_install_ctor'))
|
||||||
ctor.linkage = 'internal'
|
ctor.linkage = 'internal'
|
||||||
cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry'))
|
cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry'))
|
||||||
cb.call(install_fn, []) # Install() 无参;返回值(int)有意忽略
|
cb.call(install_fn, []) # Install() 无参;返回值(int)有意忽略
|
||||||
cb.ret_void()
|
cb.ret_void()
|
||||||
if 'llvm.global_ctors' in self.module.globals:
|
if 'llvm.global_ctors' in self.module.globals:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'--ctrlc-stack: 模块内已存在 @llvm.global_ctors,'
|
'--traceback: 模块内已存在 @llvm.global_ctors,'
|
||||||
'无法注入 dbgstack 启动期构造器(不静默降级)')
|
'无法注入 traceback 启动期构造器(不静默降级)')
|
||||||
elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p])
|
elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p])
|
||||||
arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1)
|
arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1)
|
||||||
entry_val: ir.Constant = ir.Constant(elem_ty, [
|
entry_val: ir.Constant = ir.Constant(elem_ty, [
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class ExprGenMixin:
|
|||||||
return self.builder.bitcast(global_var, ir.PointerType(ir.IntType(8)), name="str")
|
return self.builder.bitcast(global_var, ir.PointerType(ir.IntType(8)), name="str")
|
||||||
return ir.Constant(ir.IntType(32), int(value))
|
return ir.Constant(ir.IntType(32), int(value))
|
||||||
|
|
||||||
def emit_binary_op(self, op: str, left: ir.Value, right: ir.Value, is_unsigned: bool = False) -> ir.Value:
|
def emit_binary_op(self, op: str, left: ir.Value, right: ir.Value, is_unsigned: bool = False, is_aug: bool = False) -> ir.Value:
|
||||||
if isinstance(left.type, ir.PointerType) and isinstance(right.type, ir.IntType):
|
if isinstance(left.type, ir.PointerType) and isinstance(right.type, ir.IntType):
|
||||||
# 数组指针需要先 decay 为元素指针,否则 GEP 会以整个数组为步长
|
# 数组指针需要先 decay 为元素指针,否则 GEP 会以整个数组为步长
|
||||||
# 例如 [64 x i8]* + 1 会前进 64 字节而非 1 字节
|
# 例如 [64 x i8]* + 1 会前进 64 字节而非 1 字节
|
||||||
@@ -65,6 +65,46 @@ class ExprGenMixin:
|
|||||||
left = self.builder.zext(left, ir.IntType(64), name="int2ptrint2")
|
left = self.builder.zext(left, ir.IntType(64), name="int2ptrint2")
|
||||||
if op == '+' or op == 'Add':
|
if op == '+' or op == 'Add':
|
||||||
return self.builder.gep(right, [left], name="ptr_add_rev")
|
return self.builder.gep(right, [left], name="ptr_add_rev")
|
||||||
|
# ============================================================
|
||||||
|
# 双指针算术全域 fail-fast(**单一收敛点**)
|
||||||
|
#
|
||||||
|
# BinOp(HandlesExprOps._HandleBinOpLlvm)与 AugAssign
|
||||||
|
# (HandlesAugAssign._HandleAugAssignLlvm,+=、-=、*= …)都经本函数,
|
||||||
|
# 故在此一次收口,避免多点各写一份判据/报文。
|
||||||
|
# 判据:运算符属二元算术/位运算/移位集合,且**两操作数都是 LLVM 指针**。
|
||||||
|
# 本语言的 str 是 i8*,没有 `+` 拼接语义;指针 ± 指针也不是 GEP 语义
|
||||||
|
# (GEP 只接受「一指针一整数」,已在上方两个 ptr/int 分支处理并返回)。
|
||||||
|
# 落到下方 ops/shift_ops/bit_ops 会直出非法 IR(`add i8*, i8*` ⇒ opt 报
|
||||||
|
# invalid operand type for instruction),故在 IR 生成前 fail-fast。
|
||||||
|
# 判据与报文逐句对齐 TPV ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。
|
||||||
|
# ============================================================
|
||||||
|
if op in ('+', 'Add', '-', 'Sub', '*', 'Mult', '/', 'Div', '//', 'FloorDiv',
|
||||||
|
'%', 'Mod', '**', 'Pow', '&', 'BitAnd', '|', 'BitOr', '^', 'BitXor',
|
||||||
|
'<<', 'LShift', '>>', 'RShift') \
|
||||||
|
and isinstance(left.type, ir.PointerType) \
|
||||||
|
and isinstance(right.type, ir.PointerType):
|
||||||
|
LeftTy: str = str(left.type)
|
||||||
|
RightTy: str = str(right.type)
|
||||||
|
if LeftTy == 'i8*':
|
||||||
|
LeftTy = 'str(i8*)'
|
||||||
|
if RightTy == 'i8*':
|
||||||
|
RightTy = 'str(i8*)'
|
||||||
|
OpCh: str = op
|
||||||
|
AugNote: str = ''
|
||||||
|
if is_aug:
|
||||||
|
# 增量赋值(+= 等)要能自报家门,便于定位(不写成裸 '+')
|
||||||
|
OpCh = f'{op}='
|
||||||
|
AugNote = '(增量赋值)'
|
||||||
|
raise TypeError(
|
||||||
|
f"[TransPyC] 双指针运算类型非法: op = '{OpCh}'{AugNote},"
|
||||||
|
f'左操作数类型 = {LeftTy},右操作数类型 = {RightTy}。'
|
||||||
|
f'本语言的 str 是 i8*,没有 `+` 拼接语义(`str + str` 与指针 ± 指针'
|
||||||
|
f'都会退化成裸指针 add/sub ⇒ 非法 IR: opt 报 '
|
||||||
|
f'invalid operand type for instruction)。'
|
||||||
|
f'禁止静默降级,已终止编译。'
|
||||||
|
f'修法: 用 f-string 插值、string.strcat 或 viperlib.snprintf 构建字符串。'
|
||||||
|
+ self._get_node_info()
|
||||||
|
)
|
||||||
is_float: bool = isinstance(left.type, (ir.FloatType, ir.DoubleType)) or \
|
is_float: bool = isinstance(left.type, (ir.FloatType, ir.DoubleType)) or \
|
||||||
isinstance(right.type, (ir.FloatType, ir.DoubleType))
|
isinstance(right.type, (ir.FloatType, ir.DoubleType))
|
||||||
if is_float:
|
if is_float:
|
||||||
@@ -291,10 +331,14 @@ class ExprGenMixin:
|
|||||||
if isinstance(ret_type, ir.PointerType) and isinstance(value.type, ir.PointerType):
|
if isinstance(ret_type, ir.PointerType) and isinstance(value.type, ir.PointerType):
|
||||||
value = self.builder.bitcast(value, ret_type, name="ret_cast")
|
value = self.builder.bitcast(value, ret_type, name="ret_cast")
|
||||||
elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType):
|
elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType):
|
||||||
if isinstance(value.type.pointee, ir.IntType) and isinstance(ret_type, ir.IntType) and value.type.pointee.width == ret_type.width:
|
if isinstance(ret_type, ir.IntType):
|
||||||
value = self._load(value, name="ret_Load")
|
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
|
||||||
elif isinstance(ret_type, ir.IntType):
|
# 原先"同宽 load 所指物 / 其余 ptrtoint"都是静默转换。
|
||||||
value = self.builder.ptrtoint(value, ret_type, name="ret_ptrtoint")
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"指针隐式转整数已禁止:函数 {self.func.name} 声明返回 {ret_type},"
|
||||||
|
f"但 return 的是指针类型 {value.type};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
|
)
|
||||||
elif isinstance(value.type.pointee, ret_type.__class__) and not isinstance(value.type.pointee, ir.IntType):
|
elif isinstance(value.type.pointee, ret_type.__class__) and not isinstance(value.type.pointee, ir.IntType):
|
||||||
value = self._load(value, name="ret_Load")
|
value = self._load(value, name="ret_Load")
|
||||||
elif isinstance(value.type.pointee, ir.PointerType):
|
elif isinstance(value.type.pointee, ir.PointerType):
|
||||||
@@ -302,7 +346,11 @@ class ExprGenMixin:
|
|||||||
if Loaded.type == ret_type:
|
if Loaded.type == ret_type:
|
||||||
value = Loaded
|
value = Loaded
|
||||||
elif isinstance(ret_type, ir.IntType):
|
elif isinstance(ret_type, ir.IntType):
|
||||||
value = self.builder.ptrtoint(Loaded, ret_type, name="ret_ptrtoint")
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
|
raise FailFastError(
|
||||||
|
f"指针隐式转整数已禁止:函数 {self.func.name} 声明返回 {ret_type},"
|
||||||
|
f"但 return 的是指针类型 {Loaded.type};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
|
)
|
||||||
elif isinstance(ret_type, ir.IntType) and isinstance(value.type, ir.IntType):
|
elif isinstance(ret_type, ir.IntType) and isinstance(value.type, ir.IntType):
|
||||||
if value.type.width < ret_type.width:
|
if value.type.width < ret_type.width:
|
||||||
if value.type.width == 1:
|
if value.type.width == 1:
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ import re
|
|||||||
|
|
||||||
# `!N = ` 形式的编号元数据定义行
|
# `!N = ` 形式的编号元数据定义行
|
||||||
_Definition = re.compile(r'^!(\d+) = ')
|
_Definition = re.compile(r'^!(\d+) = ')
|
||||||
# 编号元数据引用
|
|
||||||
_Reference = re.compile(r'!(\d+)')
|
|
||||||
# `!name = ...` 形式的命名元数据(首字符为字母,故不会命中 `!N = `)
|
# `!name = ...` 形式的命名元数据(首字符为字母,故不会命中 `!N = `)
|
||||||
_NamedDefinition = re.compile(r'^!([A-Za-z][\w.]*)\s*=\s*(.*)$')
|
_NamedDefinition = re.compile(r'^!([A-Za-z][\w.]*)\s*=\s*(.*)$')
|
||||||
# `!{ ... }` 列表形式
|
# `!{ ... }` 列表形式
|
||||||
_ListForm = re.compile(r'^!\{(.*)\}$', re.DOTALL)
|
_ListForm = re.compile(r'^!\{(.*)\}$', re.DOTALL)
|
||||||
# 纯 id 引用 `!N`
|
# 纯 id 引用 `!N`
|
||||||
_PureId = re.compile(r'^!(\d+)$')
|
_PureId = re.compile(r'^!(\d+)$')
|
||||||
|
# Decimal digit set for the no-regex fast rewrite path.
|
||||||
|
_DIGITS: frozenset[str] = frozenset('0123456789')
|
||||||
|
|
||||||
|
|
||||||
def _SplitTopLevel(Content: str) -> list[str]:
|
def _SplitTopLevel(Content: str) -> list[str]:
|
||||||
@@ -78,47 +78,69 @@ def _ListItems(Rhs: str) -> list[str] | None:
|
|||||||
return _SplitTopLevel(m.group(1))
|
return _SplitTopLevel(m.group(1))
|
||||||
|
|
||||||
|
|
||||||
def _RewriteReferences(Line: str, Mapping: dict[int, int]) -> str:
|
def _RewritePlain(Line: str, StrMap: dict[str, str]) -> str:
|
||||||
"""改写一行里对编号元数据的引用,跳过字符串字面量与 `;` 注释。"""
|
"""Rewrite `!N` references in a line that has no `"` and no `;`.
|
||||||
if '!' not in Line:
|
|
||||||
|
Equivalent to a regex substitution of `!` + digits but without the regex
|
||||||
|
engine callback: split on '!' (C-level), map each digit run by full token,
|
||||||
|
keep unknown ids verbatim.
|
||||||
|
"""
|
||||||
|
Parts: list[str] = Line.split('!')
|
||||||
|
if len(Parts) == 1:
|
||||||
return Line
|
return Line
|
||||||
if '"' not in Line and ';' not in Line:
|
Out: list[str] = [Parts[0]]
|
||||||
# 元数据行绝大多数不含字符串/注释:整体正则替换,避免逐字符扫描
|
Ap = Out.append
|
||||||
return _Reference.sub(
|
Get = StrMap.get
|
||||||
lambda m: '!' + str(Mapping.get(int(m.group(1)), int(m.group(1)))), Line)
|
for Part in Parts[1:]:
|
||||||
Out: list[str] = []
|
d: int = 0
|
||||||
|
n: int = len(Part)
|
||||||
|
while d < n and Part[d] in _DIGITS:
|
||||||
|
d += 1
|
||||||
|
if not d:
|
||||||
|
Ap('!')
|
||||||
|
Ap(Part)
|
||||||
|
continue
|
||||||
|
Digits: str = Part[:d]
|
||||||
|
# Leading zeros normalise the way int()/str() did in the old path.
|
||||||
|
Tok: str = '!' + Digits if (d == 1 or Digits[0] != '0') else '!' + str(int(Digits))
|
||||||
|
New: str | None = Get(Tok)
|
||||||
|
Ap(Tok if New is None else New)
|
||||||
|
Ap(Part[d:])
|
||||||
|
return ''.join(Out)
|
||||||
|
|
||||||
|
|
||||||
|
def _RewriteQuoted(Line: str, StrMap: dict[str, str]) -> str:
|
||||||
|
"""Rewrite `!N` refs in a line that contains `"` and/or `;`.
|
||||||
|
|
||||||
|
String literals and the trailing `;` comment are copied verbatim; the
|
||||||
|
remaining (quote/comment free) chunks go through `_RewritePlain`. Uses
|
||||||
|
C-level str.find instead of the old per-character Python scan.
|
||||||
|
"""
|
||||||
|
Out: list[str] = []; Ap = Out.append
|
||||||
i: int = 0
|
i: int = 0
|
||||||
n: int = len(Line)
|
n: int = len(Line)
|
||||||
while i < n:
|
while i < n:
|
||||||
Ch: str = Line[i]
|
q: int = Line.find('"', i)
|
||||||
if Ch == '"':
|
s: int = Line.find(';', i, q if q >= 0 else n)
|
||||||
j: int = i + 1
|
if s >= 0:
|
||||||
while j < n:
|
Ap(_RewritePlain(Line[i:s], StrMap))
|
||||||
if Line[j] == '\\':
|
Ap(Line[s:]) # ';' comment: copy verbatim to end of line
|
||||||
j += 2
|
|
||||||
continue
|
|
||||||
if Line[j] == '"':
|
|
||||||
j += 1
|
|
||||||
break
|
|
||||||
j += 1
|
|
||||||
Out.append(Line[i:j])
|
|
||||||
i = j
|
|
||||||
continue
|
|
||||||
if Ch == ';':
|
|
||||||
Out.append(Line[i:])
|
|
||||||
break
|
break
|
||||||
if Ch == '!':
|
if q < 0:
|
||||||
m = _Reference.match(Line, i)
|
Ap(_RewritePlain(Line[i:], StrMap))
|
||||||
if m:
|
break
|
||||||
New: int | None = Mapping.get(int(m.group(1)))
|
Ap(_RewritePlain(Line[i:q], StrMap))
|
||||||
Out.append('!' + str(New) if New is not None else m.group(0))
|
j: int = q + 1
|
||||||
i = m.end()
|
while j < n:
|
||||||
|
if Line[j] == '\\':
|
||||||
|
j += 2
|
||||||
continue
|
continue
|
||||||
Out.append(Ch)
|
if Line[j] == '"':
|
||||||
i += 1
|
j += 1
|
||||||
continue
|
break
|
||||||
Out.append(Ch)
|
j += 1
|
||||||
i += 1
|
Ap(Line[q:j]) # string literal: copy verbatim
|
||||||
|
i = j
|
||||||
return ''.join(Out)
|
return ''.join(Out)
|
||||||
|
|
||||||
|
|
||||||
@@ -139,7 +161,7 @@ def SanitizeIRMetadata(text: str) -> str:
|
|||||||
i: int = 0
|
i: int = 0
|
||||||
while i < LineCount:
|
while i < LineCount:
|
||||||
if Segments[i].startswith('!'):
|
if Segments[i].startswith('!'):
|
||||||
j: int = i
|
j: int = i + 1
|
||||||
while j < LineCount and Segments[j].startswith('!'):
|
while j < LineCount and Segments[j].startswith('!'):
|
||||||
j += 1
|
j += 1
|
||||||
Blocks.append((i, j))
|
Blocks.append((i, j))
|
||||||
@@ -161,7 +183,13 @@ def SanitizeIRMetadata(text: str) -> str:
|
|||||||
# 3) 块内按定义出现顺序重新编号(局部 id -> 全局 id)
|
# 3) 块内按定义出现顺序重新编号(局部 id -> 全局 id)
|
||||||
Lines: list[str] = list(Segments)
|
Lines: list[str] = list(Segments)
|
||||||
BlockMaps: list[dict[int, int]] = []
|
BlockMaps: list[dict[int, int]] = []
|
||||||
|
BlockStrMaps: list[dict[str, str]] = []
|
||||||
BlockIsIdentity: list[bool] = []
|
BlockIsIdentity: list[bool] = []
|
||||||
|
# Definition line index -> (new global id, length of its rewritten "!N = " prefix).
|
||||||
|
# Lets step 4/5 skip a per-line regex to re-detect definition lines.
|
||||||
|
DefInfo: dict[int, tuple[int, int]] = {}
|
||||||
|
# Named metadata lines are collected here (only block lines can start with '!').
|
||||||
|
NamedLines: dict[str, list[int]] = {}
|
||||||
Counter: int = 0
|
Counter: int = 0
|
||||||
for Start, End in Blocks:
|
for Start, End in Blocks:
|
||||||
Mapping: dict[int, int] = {}
|
Mapping: dict[int, int] = {}
|
||||||
@@ -169,14 +197,21 @@ def SanitizeIRMetadata(text: str) -> str:
|
|||||||
for k in range(Start, End):
|
for k in range(Start, End):
|
||||||
m = _Definition.match(Lines[k])
|
m = _Definition.match(Lines[k])
|
||||||
if not m:
|
if not m:
|
||||||
|
nm = _NamedDefinition.match(Lines[k])
|
||||||
|
if nm is not None:
|
||||||
|
NamedLines.setdefault(nm.group(1), []).append(k)
|
||||||
continue
|
continue
|
||||||
Local: int = int(m.group(1))
|
Local: int = int(m.group(1))
|
||||||
Mapping[Local] = Counter
|
Mapping[Local] = Counter
|
||||||
if Local != Counter:
|
if Local != Counter:
|
||||||
Identity = False
|
Identity = False
|
||||||
Lines[k] = '!' + str(Counter) + ' = ' + Lines[k][m.end():]
|
Prefix: str = '!' + str(Counter) + ' = '
|
||||||
|
DefInfo[k] = (Counter, len(Prefix))
|
||||||
|
Lines[k] = Prefix + Lines[k][m.end():]
|
||||||
Counter += 1
|
Counter += 1
|
||||||
BlockMaps.append(Mapping)
|
BlockMaps.append(Mapping)
|
||||||
|
BlockStrMaps.append({'!' + str(LocalId): '!' + str(GlobId)
|
||||||
|
for LocalId, GlobId in Mapping.items()})
|
||||||
BlockIsIdentity.append(Identity)
|
BlockIsIdentity.append(Identity)
|
||||||
|
|
||||||
# 4) 改写全文引用(定义体右值、define/指令行的 !dbg、命名元数据列表)
|
# 4) 改写全文引用(定义体右值、define/指令行的 !dbg、命名元数据列表)
|
||||||
@@ -185,36 +220,40 @@ def SanitizeIRMetadata(text: str) -> str:
|
|||||||
# 旧 id 843 → Mapping[843]=1686),一旦改写左侧就会出现两个定义共用同一
|
# 旧 id 843 → Mapping[843]=1686),一旦改写左侧就会出现两个定义共用同一
|
||||||
# id,opt/llc 随即报 "Metadata id is already used"(本 bug 实测残留 276 个
|
# id,opt/llc 随即报 "Metadata id is already used"(本 bug 实测残留 276 个
|
||||||
# 重复 id)。故定义行只改写 '=' 之后的右值。
|
# 重复 id)。故定义行只改写 '=' 之后的右值。
|
||||||
|
GetDef = DefInfo.get
|
||||||
for k in range(LineCount):
|
for k in range(LineCount):
|
||||||
Index: int = Owner[k]
|
Index: int = Owner[k]
|
||||||
if Index < 0 or BlockIsIdentity[Index] or not BlockMaps[Index]:
|
if Index < 0 or BlockIsIdentity[Index] or not BlockMaps[Index]:
|
||||||
continue
|
continue
|
||||||
Line: str = Lines[k]
|
Line: str = Lines[k]
|
||||||
Head: re.Match | None = _Definition.match(Line)
|
if '!' not in Line:
|
||||||
if Head:
|
continue
|
||||||
Fixed: str = Line[:Head.end()] + _RewriteReferences(Line[Head.end():], BlockMaps[Index])
|
StrMap: dict[str, str] = BlockStrMaps[Index]
|
||||||
|
Def: tuple[int, int] | None = GetDef(k)
|
||||||
|
if Def is not None:
|
||||||
|
Split: int = Def[1]
|
||||||
|
Tail: str = Line[Split:]
|
||||||
|
Fixed: str = Line[:Split] + (_RewritePlain(Tail, StrMap)
|
||||||
|
if ('"' not in Tail and ';' not in Tail)
|
||||||
|
else _RewriteQuoted(Tail, StrMap))
|
||||||
else:
|
else:
|
||||||
Fixed = _RewriteReferences(Line, BlockMaps[Index])
|
Fixed = (_RewritePlain(Line, StrMap)
|
||||||
|
if ('"' not in Line and ';' not in Line)
|
||||||
|
else _RewriteQuoted(Line, StrMap))
|
||||||
if Fixed != Line:
|
if Fixed != Line:
|
||||||
Lines[k] = Fixed
|
Lines[k] = Fixed
|
||||||
|
|
||||||
# 5) 定义体索引(新 id -> 右值文本),供命名元数据判重
|
# 5) 定义体索引(新 id -> 右值文本),供命名元数据判重。
|
||||||
|
# 只遍历定义行(步骤 3 已登记),不再逐行做正则匹配。
|
||||||
BodyOf: dict[int, str] = {}
|
BodyOf: dict[int, str] = {}
|
||||||
for k in range(LineCount):
|
for k, (NewId, Split) in DefInfo.items():
|
||||||
m = _Definition.match(Lines[k])
|
BodyOf[NewId] = Lines[k][Split:].strip()
|
||||||
if m:
|
|
||||||
BodyOf[int(m.group(1))] = Lines[k][m.end():].strip()
|
|
||||||
|
|
||||||
# 6) 命名元数据:同名多份合并成一份。
|
# 6) 命名元数据:同名多份合并成一份。
|
||||||
# - !llvm.dbg.cu:全部条目聚合(每个 DICompileUnit 都必须被列出,不能去重)
|
# - !llvm.dbg.cu:全部条目聚合(每个 DICompileUnit 都必须被列出,不能去重)
|
||||||
# - 其它(!llvm.module.flags、!llvm.ident ...):条目内容(id 引用解析成
|
# - 其它(!llvm.module.flags、!llvm.ident ...):条目内容(id 引用解析成
|
||||||
# 节点正文)完全相同的多份只留一份,不同则聚合。
|
# 节点正文)完全相同的多份只留一份,不同则聚合。
|
||||||
NamedLines: dict[str, list[int]] = {}
|
# NamedLines 已在步骤 3 随块扫描一并收集(命名行的名字不受引用改写影响)。
|
||||||
for k in range(LineCount):
|
|
||||||
m = _NamedDefinition.match(Lines[k])
|
|
||||||
if m:
|
|
||||||
NamedLines.setdefault(m.group(1), []).append(k)
|
|
||||||
|
|
||||||
Dropped: set[int] = set()
|
Dropped: set[int] = set()
|
||||||
for Name, Indices in NamedLines.items():
|
for Name, Indices in NamedLines.items():
|
||||||
if len(Indices) == 1 and Name != 'llvm.dbg.cu':
|
if len(Indices) == 1 and Name != 'llvm.dbg.cu':
|
||||||
@@ -225,20 +264,25 @@ def SanitizeIRMetadata(text: str) -> str:
|
|||||||
SeenItems: set[str] = set()
|
SeenItems: set[str] = set()
|
||||||
SeenBodies: set[tuple[str, ...]] = set()
|
SeenBodies: set[tuple[str, ...]] = set()
|
||||||
Single: bool = True
|
Single: bool = True
|
||||||
|
IsDbgCu: bool = Name == 'llvm.dbg.cu'
|
||||||
for k in Indices:
|
for k in Indices:
|
||||||
Rhs: str = _NamedDefinition.match(Lines[k]).group(2).strip()
|
NamedMatch = _NamedDefinition.match(Lines[k])
|
||||||
|
Rhs: str = (NamedMatch.group(2) if NamedMatch is not None else '').strip()
|
||||||
ListItems: list[str] | None = _ListItems(Rhs)
|
ListItems: list[str] | None = _ListItems(Rhs)
|
||||||
if ListItems is None:
|
if ListItems is None:
|
||||||
Single = False
|
Single = False
|
||||||
break
|
break
|
||||||
Bodies: tuple[str, ...] = tuple(
|
if not IsDbgCu:
|
||||||
BodyOf.get(int(_PureId.match(Item).group(1)), Item) if _PureId.match(Item) else Item
|
# !llvm.dbg.cu never de-dupes by body, so skip resolving bodies for it.
|
||||||
for Item in ListItems
|
Bodies: list[str] = []
|
||||||
)
|
for Item in ListItems:
|
||||||
if Name != 'llvm.dbg.cu':
|
PureMatch = _PureId.match(Item)
|
||||||
if Bodies in SeenBodies:
|
Bodies.append(BodyOf.get(int(PureMatch.group(1)), Item)
|
||||||
|
if PureMatch else Item)
|
||||||
|
Key: tuple[str, ...] = tuple(Bodies)
|
||||||
|
if Key in SeenBodies:
|
||||||
continue
|
continue
|
||||||
SeenBodies.add(Bodies)
|
SeenBodies.add(Key)
|
||||||
for Item in ListItems:
|
for Item in ListItems:
|
||||||
if Item not in SeenItems:
|
if Item not in SeenItems:
|
||||||
SeenItems.add(Item)
|
SeenItems.add(Item)
|
||||||
|
|||||||
@@ -2,6 +2,50 @@ from __future__ import annotations
|
|||||||
import llvmlite.ir as ir
|
import llvmlite.ir as ir
|
||||||
|
|
||||||
|
|
||||||
|
class _VolatileLoadInstr(ir.LoadInstr):
|
||||||
|
"""带 LLVM `volatile` 限定符的 load 指令。
|
||||||
|
|
||||||
|
llvmlite 0.47 的 `ir.LoadInstr` / `ir.StoreInstr` 不支持 volatile(无该属性,
|
||||||
|
`descr` 也不输出该关键字),故按本仓库既有做法(BaseGen.VaArgInstruction
|
||||||
|
自定义 `descr`)派生子类,只把 `load` 关键字扩成 `load volatile`。
|
||||||
|
输出格式与 llvmlite 原始实现逐字段一致(类型、指针、align、元数据),
|
||||||
|
仅多一个 `volatile` 关键字;语义与 TransPyV 侧 `build_load(..., vol)` 对齐。
|
||||||
|
"""
|
||||||
|
|
||||||
|
def descr(self, buf: list[str]) -> None:
|
||||||
|
[val] = self.operands
|
||||||
|
if self.align is not None:
|
||||||
|
align = ', align %d' % (self.align)
|
||||||
|
else:
|
||||||
|
align = ''
|
||||||
|
buf.append("load volatile {0}, {1} {2}{3}{4}\n".format(
|
||||||
|
self.type,
|
||||||
|
val.type,
|
||||||
|
val.get_reference(),
|
||||||
|
align,
|
||||||
|
self._stringify_metadata(leading_comma=True),
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class _VolatileStoreInstr(ir.StoreInstr):
|
||||||
|
"""带 LLVM `volatile` 限定符的 store 指令(与 _VolatileLoadInstr 同款理由)。"""
|
||||||
|
|
||||||
|
def descr(self, buf: list[str]) -> None:
|
||||||
|
val, ptr = self.operands
|
||||||
|
if self.align is not None:
|
||||||
|
align = ', align %d' % (self.align)
|
||||||
|
else:
|
||||||
|
align = ''
|
||||||
|
buf.append("store volatile {0} {1}, {2} {3}{4}{5}\n".format(
|
||||||
|
val.type,
|
||||||
|
val.get_reference(),
|
||||||
|
ptr.type,
|
||||||
|
ptr.get_reference(),
|
||||||
|
align,
|
||||||
|
self._stringify_metadata(leading_comma=True),
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
class MemoryOpsMixin:
|
class MemoryOpsMixin:
|
||||||
|
|
||||||
def _register_local_heap_ptr(self, val: ir.Value, ClassName: str | None = None, VarName: str | None = None) -> None:
|
def _register_local_heap_ptr(self, val: ir.Value, ClassName: str | None = None, VarName: str | None = None) -> None:
|
||||||
@@ -126,12 +170,42 @@ class MemoryOpsMixin:
|
|||||||
self.builder.position_at_end(saved_block)
|
self.builder.position_at_end(saved_block)
|
||||||
return instr
|
return instr
|
||||||
|
|
||||||
def _load(self, ptr: ir.Value, name: str = '', align: int | None = None) -> ir.LoadInstr:
|
def _EmitLoad(self, ptr: ir.Value, name: str = '', align: int | None = None,
|
||||||
|
volatile: bool = False) -> ir.Value:
|
||||||
|
"""发射一条 load;volatile=True 时走 _VolatileLoadInstr(LLVM `load volatile`)。
|
||||||
|
|
||||||
|
类型推断完全交给 LoadInstr 自身(与 llvmlite 原始路径同一套逻辑),
|
||||||
|
不额外兜底:推不出类型时与原生 `builder.load` 一样直接抛错。
|
||||||
|
"""
|
||||||
|
if not volatile:
|
||||||
|
return self.builder.load(ptr, name=name, align=align)
|
||||||
|
instr: ir.LoadInstr = _VolatileLoadInstr(self.builder.block, ptr, name=name)
|
||||||
|
instr.align = align
|
||||||
|
self.builder._insert(instr)
|
||||||
|
return instr
|
||||||
|
|
||||||
|
def _EmitStore(self, val: ir.Value, ptr: ir.Value, align: int | None = None,
|
||||||
|
volatile: bool = False) -> ir.StoreInstr:
|
||||||
|
"""发射一条 store;volatile=True 时走 _VolatileStoreInstr(LLVM `store volatile`)。"""
|
||||||
|
if not volatile:
|
||||||
|
return self.builder.store(val, ptr, align=align)
|
||||||
|
instr: ir.StoreInstr = _VolatileStoreInstr(self.builder.block, val, ptr)
|
||||||
|
instr.align = align
|
||||||
|
self.builder._insert(instr)
|
||||||
|
return instr
|
||||||
|
|
||||||
|
def _load(self, ptr: ir.Value, name: str = '', align: int | None = None,
|
||||||
|
volatile: bool | None = None) -> ir.LoadInstr:
|
||||||
if align is None:
|
if align is None:
|
||||||
if isinstance(ptr.type, ir.PointerType):
|
if isinstance(ptr.type, ir.PointerType):
|
||||||
align = self._get_align(ptr.type.pointee)
|
align = self._get_align(ptr.type.pointee)
|
||||||
else:
|
else:
|
||||||
align = 0
|
align = 0
|
||||||
|
# volatile=None ⇒ 按登记名自动判定(变量自身/所指物/字段,见 _IsPtrVolatile);
|
||||||
|
# 显式传入 True/False 时以调用方为准。判定必须先于下方 bitcast(bitcast
|
||||||
|
# 结果的名字不再是变量名,会丢登记名)。
|
||||||
|
if volatile is None:
|
||||||
|
volatile = self._IsPtrVolatile(ptr)
|
||||||
if isinstance(ptr.type, ir.PointerType):
|
if isinstance(ptr.type, ir.PointerType):
|
||||||
pointee_type: ir.Type = ptr.type.pointee
|
pointee_type: ir.Type = ptr.type.pointee
|
||||||
if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths:
|
if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths:
|
||||||
@@ -139,8 +213,8 @@ class MemoryOpsMixin:
|
|||||||
if width > 0:
|
if width > 0:
|
||||||
actual_type: ir.IntType = ir.IntType(width)
|
actual_type: ir.IntType = ir.IntType(width)
|
||||||
bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load")
|
bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load")
|
||||||
return self.builder.load(bitcast_ptr, name=name, align=align)
|
return self._EmitLoad(bitcast_ptr, name=name, align=align, volatile=volatile)
|
||||||
return self.builder.load(ptr, name=name, align=align)
|
return self._EmitLoad(ptr, name=name, align=align, volatile=volatile)
|
||||||
|
|
||||||
def _Load_var(self, name: str) -> ir.Value | None:
|
def _Load_var(self, name: str) -> ir.Value | None:
|
||||||
if name in self._direct_values:
|
if name in self._direct_values:
|
||||||
@@ -199,17 +273,14 @@ class MemoryOpsMixin:
|
|||||||
# (此前 pointee.width == target.width 时直接 load null,
|
# (此前 pointee.width == target.width 时直接 load null,
|
||||||
# 生成 `load i32, i32* null` → ASAN access-violation)
|
# 生成 `load i32, i32* null` → ASAN access-violation)
|
||||||
return ir.Constant(target_type, 0)
|
return ir.Constant(target_type, 0)
|
||||||
if isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == target_type.width:
|
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
|
||||||
return self.builder.load(val, name="load_ptr_as_int_store")
|
# 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load
|
||||||
if target_type.width <= 8 and isinstance(val.type.pointee, ir.IntType):
|
# 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。
|
||||||
Loaded: ir.Value = self.builder.load(val, name="load_ptr_byte_store")
|
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||||
if Loaded.type != target_type:
|
raise FailFastError(
|
||||||
if isinstance(Loaded.type, ir.IntType) and isinstance(target_type, ir.IntType):
|
f"指针隐式转整数已禁止:不能把指针类型 {val.type} 赋给整数类型 {target_type};"
|
||||||
if target_type.width < Loaded.type.width:
|
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
|
||||||
return self.builder.trunc(Loaded, target_type, name="trunc_ptr_byte_store")
|
)
|
||||||
return self.builder.zext(Loaded, target_type, name="zext_ptr_byte_store")
|
|
||||||
return Loaded
|
|
||||||
return self.builder.ptrtoint(val, target_type, name="ptr_to_int_store")
|
|
||||||
if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType):
|
if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType):
|
||||||
return self.builder.fptrunc(val, target_type, name="fptrunc_store")
|
return self.builder.fptrunc(val, target_type, name="fptrunc_store")
|
||||||
if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType):
|
if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType):
|
||||||
@@ -242,12 +313,17 @@ class MemoryOpsMixin:
|
|||||||
return Loaded
|
return Loaded
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None) -> ir.StoreInstr:
|
def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None,
|
||||||
|
volatile: bool | None = None) -> ir.StoreInstr:
|
||||||
if align is None:
|
if align is None:
|
||||||
if isinstance(ptr.type, ir.PointerType):
|
if isinstance(ptr.type, ir.PointerType):
|
||||||
align = self._get_align(ptr.type.pointee)
|
align = self._get_align(ptr.type.pointee)
|
||||||
else:
|
else:
|
||||||
align = 0
|
align = 0
|
||||||
|
# volatile=None ⇒ 按登记名自动判定(见 _IsPtrVolatile)。判定先于下方任何
|
||||||
|
# ptr 重建(null_struct_ptr_cast 等),否则会丢登记名。
|
||||||
|
if volatile is None:
|
||||||
|
volatile = self._IsPtrVolatile(ptr)
|
||||||
if isinstance(ptr.type, ir.PointerType):
|
if isinstance(ptr.type, ir.PointerType):
|
||||||
target_type: ir.Type = ptr.type.pointee
|
target_type: ir.Type = ptr.type.pointee
|
||||||
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
|
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
|
||||||
@@ -261,7 +337,7 @@ class MemoryOpsMixin:
|
|||||||
val = self.builder.trunc(val, actual_type, name="trunc_typedef")
|
val = self.builder.trunc(val, actual_type, name="trunc_typedef")
|
||||||
else:
|
else:
|
||||||
val = self.builder.zext(val, actual_type, name="zext_typedef")
|
val = self.builder.zext(val, actual_type, name="zext_typedef")
|
||||||
return self.builder.store(val, bitcast_ptr, align=align)
|
return self._EmitStore(val, bitcast_ptr, align=align, volatile=volatile)
|
||||||
if val.type != target_type:
|
if val.type != target_type:
|
||||||
if isinstance(val, ir.Constant) and val.constant is None:
|
if isinstance(val, ir.Constant) and val.constant is None:
|
||||||
if isinstance(target_type, ir.PointerType):
|
if isinstance(target_type, ir.PointerType):
|
||||||
@@ -280,7 +356,7 @@ class MemoryOpsMixin:
|
|||||||
else:
|
else:
|
||||||
src_info: str = self._get_node_info()
|
src_info: str = self._get_node_info()
|
||||||
raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}")
|
raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}")
|
||||||
return self.builder.store(val, ptr, align=align)
|
return self._EmitStore(val, ptr, align=align, volatile=volatile)
|
||||||
|
|
||||||
def _get_var_ptr(self, name: str) -> ir.Value | None:
|
def _get_var_ptr(self, name: str) -> ir.Value | None:
|
||||||
if name in self._reg_values:
|
if name in self._reg_values:
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ from lib.includes import t as _t
|
|||||||
class StructGenMixin:
|
class StructGenMixin:
|
||||||
|
|
||||||
def _get_align(self, llvm_type: ir.Type) -> int:
|
def _get_align(self, llvm_type: ir.Type) -> int:
|
||||||
|
"""类型自然对齐(字节)。
|
||||||
|
|
||||||
|
与 `_get_struct_size` 同一约定:**不允许静默近似**。对齐猜错会让字段字节偏移
|
||||||
|
与 LLVM 实际布局不一致(本函数是 `_get_struct_size` 的字段偏移累加与
|
||||||
|
`_compute_field_byte_offset` 的输入),故拿不到对齐时一律响亮失败:
|
||||||
|
- opaque/未解析的 IdentifiedStructType → 曾按 ptr_size 猜;
|
||||||
|
- 未识别类型 → 曾按 4 猜。
|
||||||
|
"""
|
||||||
if isinstance(llvm_type, ir.IntType):
|
if isinstance(llvm_type, ir.IntType):
|
||||||
return max(1, llvm_type.width // 8)
|
return max(1, llvm_type.width // 8)
|
||||||
if isinstance(llvm_type, ir.FloatType):
|
if isinstance(llvm_type, ir.FloatType):
|
||||||
@@ -20,15 +28,28 @@ class StructGenMixin:
|
|||||||
return self.ptr_size
|
return self.ptr_size
|
||||||
if isinstance(llvm_type, ir.ArrayType):
|
if isinstance(llvm_type, ir.ArrayType):
|
||||||
return self._get_align(llvm_type.element)
|
return self._get_align(llvm_type.element)
|
||||||
|
if isinstance(llvm_type, ir.VectorType):
|
||||||
|
# 与 LLVM 一致:向量对齐 = 元素对齐(同 _get_struct_size 的向量口径)
|
||||||
|
return self._get_align(llvm_type.element)
|
||||||
if isinstance(llvm_type, ir.LiteralStructType):
|
if isinstance(llvm_type, ir.LiteralStructType):
|
||||||
return max((self._get_align(f) for f in llvm_type.elements), default=1)
|
return max((self._get_align(f) for f in llvm_type.elements), default=1)
|
||||||
if isinstance(llvm_type, ir.IdentifiedStructType):
|
if isinstance(llvm_type, ir.IdentifiedStructType):
|
||||||
|
resolved = self._resolve_opaque_struct(llvm_type)
|
||||||
|
if resolved is not llvm_type:
|
||||||
|
return self._get_align(resolved)
|
||||||
if llvm_type.elements is None or len(llvm_type.elements) == 0:
|
if llvm_type.elements is None or len(llvm_type.elements) == 0:
|
||||||
return self.ptr_size # opaque struct, assume pointer alignment
|
raise ValueError(
|
||||||
|
f"[FATAL][__sizeof__] 结构体 '{getattr(llvm_type, 'name', llvm_type)}' "
|
||||||
|
f"布局未就绪(opaque / 未解析到定义),拒绝按指针对齐兜底 —— "
|
||||||
|
f"对齐猜错会让字段字节偏移与 LLVM 实际布局不一致(读写落到错误字段)。"
|
||||||
|
f"请先确保该结构体已注册/可从 stub 加载后再取对齐。"
|
||||||
|
)
|
||||||
return max((self._get_align(f) for f in llvm_type.elements), default=1)
|
return max((self._get_align(f) for f in llvm_type.elements), default=1)
|
||||||
if isinstance(llvm_type, ir.VoidType):
|
if isinstance(llvm_type, ir.VoidType):
|
||||||
return 0
|
return 0
|
||||||
return 4
|
raise ValueError(
|
||||||
|
f"[FATAL][__sizeof__] 不支持的类型 {llvm_type}({type(llvm_type).__name__})"
|
||||||
|
f"无法计算对齐,拒绝以常数 4 兜底 —— 对齐猜错会让字段字节偏移与 LLVM 实际布局不一致。")
|
||||||
|
|
||||||
def _resolve_opaque_struct(self, llvm_type: ir.Type) -> ir.Type:
|
def _resolve_opaque_struct(self, llvm_type: ir.Type) -> ir.Type:
|
||||||
"""尝试从 Gen.structs 中查找已解析的同名 struct,解决跨模块 struct 类型对象不一致的问题"""
|
"""尝试从 Gen.structs 中查找已解析的同名 struct,解决跨模块 struct 类型对象不一致的问题"""
|
||||||
@@ -77,6 +98,17 @@ class StructGenMixin:
|
|||||||
return llvm_type
|
return llvm_type
|
||||||
|
|
||||||
def _get_struct_size(self, llvm_type: ir.Type) -> int:
|
def _get_struct_size(self, llvm_type: ir.Type) -> int:
|
||||||
|
"""类型字节尺寸(含字段对齐/填充)。
|
||||||
|
|
||||||
|
**不允许静默近似**:本函数的返回值会被 `X.__sizeof__()` 直接采用
|
||||||
|
(HandlesExprCall._HandleSizeofLlvm),而 memhub 的 alloc 又直接吃该值。
|
||||||
|
此前两处静默兜底会**少算尺寸**,进而按过小尺寸分配 → 越界写坏邻居块的
|
||||||
|
8 字节块头(块头 `(order<<1)|1` 被写成偶数 ⇒ reset_to 视为"已释放"跳过、
|
||||||
|
free 被 _is_valid_ptr 判无效而静默 no-op)⇒ 该块永久丢失 ⇒ 1 GiB 主池漏满:
|
||||||
|
- opaque/未解析的 IdentifiedStructType → 曾一律返回 ptr_size;
|
||||||
|
- 未识别类型(如 SIMD VectorType)→ 曾一律返回 4。
|
||||||
|
故尺寸拿不到时一律响亮失败(项目约定:类型信息缺失立即终止,不兜底)。
|
||||||
|
"""
|
||||||
if isinstance(llvm_type, ir.IntType):
|
if isinstance(llvm_type, ir.IntType):
|
||||||
return max(1, llvm_type.width // 8)
|
return max(1, llvm_type.width // 8)
|
||||||
if isinstance(llvm_type, ir.FloatType):
|
if isinstance(llvm_type, ir.FloatType):
|
||||||
@@ -87,10 +119,20 @@ class StructGenMixin:
|
|||||||
return self.ptr_size
|
return self.ptr_size
|
||||||
if isinstance(llvm_type, ir.ArrayType):
|
if isinstance(llvm_type, ir.ArrayType):
|
||||||
return self._get_struct_size(llvm_type.element) * llvm_type.count
|
return self._get_struct_size(llvm_type.element) * llvm_type.count
|
||||||
|
if isinstance(llvm_type, ir.VectorType):
|
||||||
|
# 与 LLVM 一致:元素尺寸 × 元素数,再向上取到 2 的幂(<3 x i32> = 16 字节)
|
||||||
|
elem_size: int = self._get_struct_size(llvm_type.element)
|
||||||
|
lanes: int = llvm_type.count
|
||||||
|
padded: int = 1
|
||||||
|
while padded < lanes:
|
||||||
|
padded = padded << 1
|
||||||
|
return elem_size * padded
|
||||||
if isinstance(llvm_type, ir.LiteralStructType):
|
if isinstance(llvm_type, ir.LiteralStructType):
|
||||||
total: int = 0
|
total: int = 0
|
||||||
max_align: int = 1
|
max_align: int = 1
|
||||||
for f in llvm_type.fields:
|
# llvmlite 的 LiteralStructType 只有 `.elements`(**没有** `.fields`):
|
||||||
|
# 旧代码写 `.fields` 必然 AttributeError,尺寸/对齐链在此静默断掉。
|
||||||
|
for f in llvm_type.elements:
|
||||||
fa: int = self._get_align(f)
|
fa: int = self._get_align(f)
|
||||||
fs: int = self._get_struct_size(f)
|
fs: int = self._get_struct_size(f)
|
||||||
max_align = max(max_align, fa)
|
max_align = max(max_align, fa)
|
||||||
@@ -102,22 +144,32 @@ class StructGenMixin:
|
|||||||
return total
|
return total
|
||||||
if isinstance(llvm_type, ir.IdentifiedStructType):
|
if isinstance(llvm_type, ir.IdentifiedStructType):
|
||||||
# 尝试解析 opaque struct 为已注册的版本
|
# 尝试解析 opaque struct 为已注册的版本
|
||||||
llvm_type = self._resolve_opaque_struct(llvm_type)
|
resolved = self._resolve_opaque_struct(llvm_type)
|
||||||
|
if resolved is not llvm_type:
|
||||||
|
return self._get_struct_size(resolved)
|
||||||
if llvm_type.elements is None or len(llvm_type.elements) == 0:
|
if llvm_type.elements is None or len(llvm_type.elements) == 0:
|
||||||
return self.ptr_size # opaque struct, assume pointer size
|
raise ValueError(
|
||||||
total: int = 0
|
f"[FATAL][__sizeof__] 结构体 '{getattr(llvm_type, 'name', llvm_type)}' "
|
||||||
max_align: int = 1
|
f"布局未就绪(opaque / 未解析到定义),拒绝按指针大小兜底 —— "
|
||||||
|
f"近似会少算尺寸,调用方(memhub alloc)按过小尺寸分配并越界写坏邻居块头。"
|
||||||
|
f"请先确保该结构体已注册/可从 stub 加载后再取尺寸。"
|
||||||
|
)
|
||||||
|
total2: int = 0
|
||||||
|
max_align2: int = 1
|
||||||
for f in llvm_type.elements:
|
for f in llvm_type.elements:
|
||||||
fa: int = self._get_align(f)
|
fa2: int = self._get_align(f)
|
||||||
fs: int = self._get_struct_size(f)
|
fs2: int = self._get_struct_size(f)
|
||||||
max_align = max(max_align, fa)
|
max_align2 = max(max_align2, fa2)
|
||||||
if fa > 1:
|
if fa2 > 1:
|
||||||
total = (total + fa - 1) // fa * fa
|
total2 = (total2 + fa2 - 1) // fa2 * fa2
|
||||||
total += fs
|
total2 += fs2
|
||||||
if max_align > 1:
|
if max_align2 > 1:
|
||||||
total = (total + max_align - 1) // max_align * max_align
|
total2 = (total2 + max_align2 - 1) // max_align2 * max_align2
|
||||||
return total
|
return total2
|
||||||
return 4
|
raise ValueError(
|
||||||
|
f"[FATAL][__sizeof__] 不支持的类型 {llvm_type}({type(llvm_type).__name__})"
|
||||||
|
f"无法计算尺寸,拒绝以常数兜底 —— 近似会少算尺寸并越界写坏内存池块头。"
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _strip_unnecessary_quotes(ir_str: str) -> str:
|
def _strip_unnecessary_quotes(ir_str: str) -> str:
|
||||||
@@ -140,12 +192,12 @@ class StructGenMixin:
|
|||||||
_DbgInfo = self._GetDebugInfo()
|
_DbgInfo = self._GetDebugInfo()
|
||||||
if _DbgInfo is not None:
|
if _DbgInfo is not None:
|
||||||
_DbgInfo.Finalize()
|
_DbgInfo.Finalize()
|
||||||
# Ctrl+C 调用栈(--ctrlc-stack):同样是"落成 .ll 文本之前"的模块级收尾——
|
# Ctrl+C 调用栈(--traceback):同样是"落成 .ll 文本之前"的模块级收尾——
|
||||||
# 当本模块就是 includes/dbgstack.vp 时,注入 @llvm.global_ctors 构造器调用
|
# 当本模块就是 includes/traceback.vp 时,注入 @llvm.global_ctors 构造器调用
|
||||||
# 其 Install()(见 BaseGen._RegisterDbgStackInstallViaCtor)。未启用/非该模块
|
# 其 Install()(见 BaseGen._RegisterTracebackInstallViaCtor)。未启用/非该模块
|
||||||
# 时 ctrlc_stack_ctor=False,不做任何事(零影响)。
|
# 时 traceback_ctor=False,不做任何事(零影响)。
|
||||||
if self.ctrlc_stack_ctor:
|
if self.traceback_ctor:
|
||||||
self._RegisterDbgStackInstallViaCtor()
|
self._RegisterTracebackInstallViaCtor()
|
||||||
ir_text: str
|
ir_text: str
|
||||||
try:
|
try:
|
||||||
ir_text = str(self.module)
|
ir_text = str(self.module)
|
||||||
|
|||||||
@@ -56,6 +56,16 @@ class TypeConvertMixin:
|
|||||||
if resolved is not None:
|
if resolved is not None:
|
||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
|
# 枚举(CEnum)类型注解 `x: Tri`:宽度 = 该枚举的存储宽度(最宽成员类型),
|
||||||
|
# 与成员常量发射点同点(ABI:两侧必须同宽)。默认宽度 32 时与改前逐字一致。
|
||||||
|
# 走 IsTypedef 之前的分支不受影响(typedef 形式枚举先被上方解析)。
|
||||||
|
if type_info.IsEnum and type_info.Name and not type_info.IsRenum:
|
||||||
|
_enum_ty: ir.Type = self._enum_int_type(type_info.Name)
|
||||||
|
_enum_result: ir.Type = _enum_ty
|
||||||
|
for _ in range(type_info.PtrCount):
|
||||||
|
_enum_result = ir.PointerType(_enum_result)
|
||||||
|
return _enum_result
|
||||||
|
|
||||||
# REnum 类型:通过 Name 查找或创建结构体(BaseType 可能为 None)
|
# REnum 类型:通过 Name 查找或创建结构体(BaseType 可能为 None)
|
||||||
# 这对 BinOp `LLVMType | t.CPtr` 合并后的 CTypeInfo 至关重要
|
# 这对 BinOp `LLVMType | t.CPtr` 合并后的 CTypeInfo 至关重要
|
||||||
if type_info.IsRenum and type_info.Name:
|
if type_info.IsRenum and type_info.Name:
|
||||||
@@ -146,6 +156,9 @@ class TypeConvertMixin:
|
|||||||
enum_name: str = type_info.Name if type_info else ''
|
enum_name: str = type_info.Name if type_info else ''
|
||||||
if enum_name and enum_name in self.structs:
|
if enum_name and enum_name in self.structs:
|
||||||
return self.structs[enum_name]
|
return self.structs[enum_name]
|
||||||
|
if isinstance(BaseType, _t.CEnum):
|
||||||
|
# CEnum 存储宽度 = 最宽成员类型(REnum 仍走上方结构体路径)
|
||||||
|
return self._enum_int_type(enum_name)
|
||||||
return ir.IntType(32)
|
return ir.IntType(32)
|
||||||
|
|
||||||
if isinstance(BaseType, _t.CUnion):
|
if isinstance(BaseType, _t.CUnion):
|
||||||
@@ -510,14 +523,16 @@ class TypeConvertMixin:
|
|||||||
return ir.PointerType(_cls_struct)
|
return ir.PointerType(_cls_struct)
|
||||||
return _cls_struct
|
return _cls_struct
|
||||||
if _Entry and _Entry.IsEnum:
|
if _Entry and _Entry.IsEnum:
|
||||||
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
|
_enum_ty: ir.Type = self._enum_int_type(type_str, _Entry)
|
||||||
|
return ir.PointerType(_enum_ty) if IsPtr else _enum_ty
|
||||||
if _Entry and _Entry.IsExceptionClass:
|
if _Entry and _Entry.IsExceptionClass:
|
||||||
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
|
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
|
||||||
if _Entry and _Entry.IsFunction:
|
if _Entry and _Entry.IsFunction:
|
||||||
return ir.PointerType(ir.IntType(8))
|
return ir.PointerType(ir.IntType(8))
|
||||||
if _Entry and _Entry.BaseType:
|
if _Entry and _Entry.BaseType:
|
||||||
if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum:
|
if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum:
|
||||||
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
|
_enum_ty2: ir.Type = self._enum_int_type(type_str, _Entry)
|
||||||
|
return ir.PointerType(_enum_ty2) if IsPtr else _enum_ty2
|
||||||
for ClassName in self.structs:
|
for ClassName in self.structs:
|
||||||
if type_str == ClassName:
|
if type_str == ClassName:
|
||||||
if IsPtr:
|
if IsPtr:
|
||||||
@@ -564,14 +579,16 @@ class TypeConvertMixin:
|
|||||||
return ir.PointerType(opaque_lp)
|
return ir.PointerType(opaque_lp)
|
||||||
return opaque_lp
|
return opaque_lp
|
||||||
if _Entry and _Entry.IsEnum:
|
if _Entry and _Entry.IsEnum:
|
||||||
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
|
_enum_ty3: ir.Type = self._enum_int_type(LastPart, _Entry)
|
||||||
|
return ir.PointerType(_enum_ty3) if IsPtr else _enum_ty3
|
||||||
if _Entry and _Entry.IsExceptionClass:
|
if _Entry and _Entry.IsExceptionClass:
|
||||||
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
|
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
|
||||||
if _Entry and _Entry.IsFunction:
|
if _Entry and _Entry.IsFunction:
|
||||||
return ir.PointerType(ir.IntType(8))
|
return ir.PointerType(ir.IntType(8))
|
||||||
if _Entry and _Entry.BaseType:
|
if _Entry and _Entry.BaseType:
|
||||||
if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum:
|
if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum:
|
||||||
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
|
_enum_ty4: ir.Type = self._enum_int_type(LastPart, _Entry)
|
||||||
|
return ir.PointerType(_enum_ty4) if IsPtr else _enum_ty4
|
||||||
if LastPart in self.structs:
|
if LastPart in self.structs:
|
||||||
if IsPtr:
|
if IsPtr:
|
||||||
return ir.PointerType(self.structs[LastPart])
|
return ir.PointerType(self.structs[LastPart])
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from dataclasses import dataclass, field
|
|||||||
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
|
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
|
||||||
import vast as ast
|
import vast as ast
|
||||||
|
|
||||||
|
from lib.core.SymbolUtils import _ENUM_DEFAULT_INT_WIDTH
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from lib.core.Handles.HandlesBase import CTypeInfo
|
from lib.core.Handles.HandlesBase import CTypeInfo
|
||||||
|
|
||||||
@@ -18,6 +20,10 @@ class ClassSymbolData:
|
|||||||
is_cpython_object: bool = False
|
is_cpython_object: bool = False
|
||||||
is_packed: bool = False
|
is_packed: bool = False
|
||||||
enum_members: List[Tuple[str, int, int]] = field(default_factory=list) # (name, value, lineno)
|
enum_members: List[Tuple[str, int, int]] = field(default_factory=list) # (name, value, lineno)
|
||||||
|
# 枚举存储宽度(bit):按"最宽成员类型"推导(SymbolUtils.EnumClassIntWidth)。
|
||||||
|
# 仅 type_kind == 'enum' 有意义;非枚举 / 无可解析整数成员 → 默认 32。
|
||||||
|
# 该属性随符号表条目序列化(_shared_sym.json pickle 的 _sm._extra)⇒ 跨模块同宽。
|
||||||
|
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from lib.core.SymbolData import (
|
|||||||
)
|
)
|
||||||
from lib.core.Handles.HandlesBase import CTypeInfo
|
from lib.core.Handles.HandlesBase import CTypeInfo
|
||||||
from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName
|
from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName
|
||||||
|
from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH
|
||||||
from lib.includes import t
|
from lib.includes import t
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ class ASTSymbolExtractor:
|
|||||||
"""提取文件中的所有符号信息"""
|
"""提取文件中的所有符号信息"""
|
||||||
with open(file_path, 'r', encoding='utf-8') as f:
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
code: str = f.read()
|
code: str = f.read()
|
||||||
tree: ast.Module = ast.parse(code)
|
tree: ast.Module = ast.parse(code, file_path)
|
||||||
|
|
||||||
result: ModuleSymbols = ModuleSymbols(file_path=file_path)
|
result: ModuleSymbols = ModuleSymbols(file_path=file_path)
|
||||||
|
|
||||||
@@ -192,9 +193,16 @@ class ASTSymbolExtractor:
|
|||||||
type_kind = 'exception'
|
type_kind = 'exception'
|
||||||
break
|
break
|
||||||
|
|
||||||
# 提取枚举成员
|
# 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端
|
||||||
|
# HandlesClassDef._RegisterEnumMembers / TPV ClassGenEnum 同一规则源)。
|
||||||
|
# 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源
|
||||||
|
# (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle),
|
||||||
|
# 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读
|
||||||
|
# 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。
|
||||||
enum_members: list[tuple[str, int, int]] = []
|
enum_members: list[tuple[str, int, int]] = []
|
||||||
|
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
|
||||||
if type_kind == 'enum':
|
if type_kind == 'enum':
|
||||||
|
enum_width = EnumClassIntWidth(node.body, self._symtab)
|
||||||
next_enum_value: int = 0
|
next_enum_value: int = 0
|
||||||
for item in node.body:
|
for item in node.body:
|
||||||
if isinstance(item, ast.Assign):
|
if isinstance(item, ast.Assign):
|
||||||
@@ -257,6 +265,7 @@ class ASTSymbolExtractor:
|
|||||||
is_cpython_object=is_cpython_object,
|
is_cpython_object=is_cpython_object,
|
||||||
is_packed=is_packed,
|
is_packed=is_packed,
|
||||||
enum_members=enum_members,
|
enum_members=enum_members,
|
||||||
|
enum_width=enum_width,
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|||||||
@@ -27,9 +27,13 @@ class SymbolInserter:
|
|||||||
|
|
||||||
# 插入类符号
|
# 插入类符号
|
||||||
for cls in module_symbols.classes:
|
for cls in module_symbols.classes:
|
||||||
|
# 枚举存储宽度("最宽成员类型")随条目登记:本条通路是跨模块符号表的
|
||||||
|
# 来源(.vpi/.pyi → LoadModuleSymbols),宽度必须一路带到序列化产物。
|
||||||
|
_enum_width: int | None = cls.enum_width if cls.type_kind == 'enum' else None
|
||||||
self._symtab._InsertClassSymbol(
|
self._symtab._InsertClassSymbol(
|
||||||
cls.name, cls.type_kind, cls.lineno, file_path,
|
cls.name, cls.type_kind, cls.lineno, file_path,
|
||||||
cls.members, cls.is_cpython_object, cls.is_packed
|
cls.members, cls.is_cpython_object, cls.is_packed,
|
||||||
|
EnumWidth=_enum_width
|
||||||
)
|
)
|
||||||
loaded.append(cls.name)
|
loaded.append(cls.name)
|
||||||
if self._namespace:
|
if self._namespace:
|
||||||
@@ -38,10 +42,10 @@ class SymbolInserter:
|
|||||||
# 插入枚举成员(短名 + 类名.成员名)—— CEnum 和 REnum 共用
|
# 插入枚举成员(短名 + 类名.成员名)—— CEnum 和 REnum 共用
|
||||||
if cls.type_kind in ('enum', 'renum'):
|
if cls.type_kind in ('enum', 'renum'):
|
||||||
for member_name, member_value, member_lineno in cls.enum_members:
|
for member_name, member_value, member_lineno in cls.enum_members:
|
||||||
self._symtab._InsertEnumMemberSymbol(member_name, cls.name, member_lineno, file_path, value=member_value)
|
self._symtab._InsertEnumMemberSymbol(member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
|
||||||
loaded.append(member_name)
|
loaded.append(member_name)
|
||||||
class_member_name: str = f"{cls.name}.{member_name}"
|
class_member_name: str = f"{cls.name}.{member_name}"
|
||||||
self._symtab._InsertEnumMemberSymbol(class_member_name, cls.name, member_lineno, file_path, value=member_value)
|
self._symtab._InsertEnumMemberSymbol(class_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
|
||||||
loaded.append(class_member_name)
|
loaded.append(class_member_name)
|
||||||
# REnum: 记录变体名列表到类符号(供 HandlesExprCall 跨模块变体构造识别)
|
# REnum: 记录变体名列表到类符号(供 HandlesExprCall 跨模块变体构造识别)
|
||||||
if cls.type_kind == 'renum':
|
if cls.type_kind == 'renum':
|
||||||
|
|||||||
@@ -28,9 +28,13 @@ class PackageReexporter:
|
|||||||
# 插入类符号(前缀.类名)
|
# 插入类符号(前缀.类名)
|
||||||
for cls in module_symbols.classes:
|
for cls in module_symbols.classes:
|
||||||
full_name: str = f"{prefix}.{cls.name}"
|
full_name: str = f"{prefix}.{cls.name}"
|
||||||
|
# 枚举宽度同样带到"模块限定名"条目上(跨模块类型注解按
|
||||||
|
# <sha1>.Enum / module.Enum 形态查表,与短名条目同宽)。
|
||||||
|
_enum_width: int | None = cls.enum_width if cls.type_kind == 'enum' else None
|
||||||
self._symtab._InsertClassSymbol(
|
self._symtab._InsertClassSymbol(
|
||||||
full_name, cls.type_kind, cls.lineno, file_path,
|
full_name, cls.type_kind, cls.lineno, file_path,
|
||||||
cls.members, cls.is_cpython_object, cls.is_packed
|
cls.members, cls.is_cpython_object, cls.is_packed,
|
||||||
|
EnumWidth=_enum_width
|
||||||
)
|
)
|
||||||
loaded.append(full_name)
|
loaded.append(full_name)
|
||||||
if self._namespace:
|
if self._namespace:
|
||||||
@@ -40,11 +44,11 @@ class PackageReexporter:
|
|||||||
if cls.type_kind in ('enum', 'renum'):
|
if cls.type_kind in ('enum', 'renum'):
|
||||||
for member_name, member_value, member_lineno in cls.enum_members:
|
for member_name, member_value, member_lineno in cls.enum_members:
|
||||||
full_member_name: str = f"{prefix}.{member_name}"
|
full_member_name: str = f"{prefix}.{member_name}"
|
||||||
self._symtab._InsertEnumMemberSymbol(full_member_name, cls.name, member_lineno, file_path, value=member_value)
|
self._symtab._InsertEnumMemberSymbol(full_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
|
||||||
loaded.append(full_member_name)
|
loaded.append(full_member_name)
|
||||||
# 同时注册 prefix.ClassName.member_name,以支持 module.Class.MEMBER 三层访问
|
# 同时注册 prefix.ClassName.member_name,以支持 module.Class.MEMBER 三层访问
|
||||||
qualified_member_name: str = f"{prefix}.{cls.name}.{member_name}"
|
qualified_member_name: str = f"{prefix}.{cls.name}.{member_name}"
|
||||||
self._symtab._InsertEnumMemberSymbol(qualified_member_name, cls.name, member_lineno, file_path, value=member_value)
|
self._symtab._InsertEnumMemberSymbol(qualified_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
|
||||||
loaded.append(qualified_member_name)
|
loaded.append(qualified_member_name)
|
||||||
# REnum: 记录变体名列表到前缀类符号
|
# REnum: 记录变体名列表到前缀类符号
|
||||||
if cls.type_kind == 'renum':
|
if cls.type_kind == 'renum':
|
||||||
|
|||||||
+10
-2
@@ -417,7 +417,7 @@ class SymbolTable:
|
|||||||
# 符号插入方法(供 SymbolInserter / PackageReexporter 调用)
|
# 符号插入方法(供 SymbolInserter / PackageReexporter 调用)
|
||||||
# ==================================================================
|
# ==================================================================
|
||||||
|
|
||||||
def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict[str, CTypeInfo], IsCpythonObject: bool, IsPacked: bool = False) -> None:
|
def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict[str, CTypeInfo], IsCpythonObject: bool, IsPacked: bool = False, EnumWidth: int | None = None) -> None:
|
||||||
info: CTypeInfo = CTypeInfo()
|
info: CTypeInfo = CTypeInfo()
|
||||||
info.Lineno = lineno
|
info.Lineno = lineno
|
||||||
info.file = FilePath
|
info.file = FilePath
|
||||||
@@ -429,6 +429,10 @@ class SymbolTable:
|
|||||||
info.IsUnion = True
|
info.IsUnion = True
|
||||||
elif TypeKind == 'enum':
|
elif TypeKind == 'enum':
|
||||||
info.IsEnum = True
|
info.IsEnum = True
|
||||||
|
# 枚举存储宽度("最宽成员类型"):登记到条目上 → 随符号表序列化
|
||||||
|
# (ToDict/pickle 的 _sm._extra)→ 跨模块读回同宽;未登记 → 默认 32。
|
||||||
|
if EnumWidth:
|
||||||
|
info.set('enum_width', int(EnumWidth))
|
||||||
elif TypeKind == 'renum':
|
elif TypeKind == 'renum':
|
||||||
info.IsStruct = True
|
info.IsStruct = True
|
||||||
info.IsRenum = True
|
info.IsRenum = True
|
||||||
@@ -444,7 +448,7 @@ class SymbolTable:
|
|||||||
|
|
||||||
self._symbols[FullName] = info
|
self._symbols[FullName] = info
|
||||||
|
|
||||||
def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str, value: int | None = None) -> None:
|
def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str, value: int | None = None, EnumWidth: int | None = None) -> None:
|
||||||
info: CTypeInfo = CTypeInfo()
|
info: CTypeInfo = CTypeInfo()
|
||||||
info.IsEnumMember = True
|
info.IsEnumMember = True
|
||||||
info.EnumName = EnumName
|
info.EnumName = EnumName
|
||||||
@@ -452,6 +456,10 @@ class SymbolTable:
|
|||||||
info.file = FilePath
|
info.file = FilePath
|
||||||
if value is not None:
|
if value is not None:
|
||||||
info.value = value
|
info.value = value
|
||||||
|
# 成员条目同样携带枚举宽度:读取端(_enum_int_type)优先用调用点手头的
|
||||||
|
# 成员条目取宽,跨模块下成员 key 可能是模块限定名,不依赖枚举条目名可达。
|
||||||
|
if EnumWidth:
|
||||||
|
info.set('enum_width', int(EnumWidth))
|
||||||
self._symbols[FullName] = info
|
self._symbols[FullName] = info
|
||||||
|
|
||||||
def _InsertTypedefSymbol(self, FullName: str, OriginalType_kind: str | None, OriginalClass: CTypeInfo | str | None, lineno: int, FilePath: str, members: dict[str, CTypeInfo] | None = None) -> None:
|
def _InsertTypedefSymbol(self, FullName: str, OriginalType_kind: str | None, OriginalClass: CTypeInfo | str | None, lineno: int, FilePath: str, members: dict[str, CTypeInfo] | None = None) -> None:
|
||||||
|
|||||||
+1543
-9
File diff suppressed because it is too large.
Load diff
@@ -198,7 +198,7 @@ class AnnotationLoaderMixin:
|
|||||||
with open(ModuleName, 'r', encoding='utf-8') as f:
|
with open(ModuleName, 'r', encoding='utf-8') as f:
|
||||||
ann_content: str = f.read()
|
ann_content: str = f.read()
|
||||||
ann_content = ExpandSoftKeywords(ann_content)
|
ann_content = ExpandSoftKeywords(ann_content)
|
||||||
ann_tree: ast.Module = ast.parse(ann_content)
|
ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
|
||||||
for node in ann_tree.body:
|
for node in ann_tree.body:
|
||||||
if isinstance(node, ast.ClassDef):
|
if isinstance(node, ast.ClassDef):
|
||||||
ClassName: str = node.name
|
ClassName: str = node.name
|
||||||
@@ -239,7 +239,7 @@ class AnnotationLoaderMixin:
|
|||||||
with open(ModuleName, 'r', encoding='utf-8') as f:
|
with open(ModuleName, 'r', encoding='utf-8') as f:
|
||||||
ann_content: str = f.read()
|
ann_content: str = f.read()
|
||||||
ann_content = ExpandSoftKeywords(ann_content)
|
ann_content = ExpandSoftKeywords(ann_content)
|
||||||
ann_tree: ast.Module = ast.parse(ann_content)
|
ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
|
||||||
for node in ann_tree.body:
|
for node in ann_tree.body:
|
||||||
# 检测 xxx: t.CEnum 形式的枚举成员变量
|
# 检测 xxx: t.CEnum 形式的枚举成员变量
|
||||||
if isinstance(node, ast.AnnAssign):
|
if isinstance(node, ast.AnnAssign):
|
||||||
@@ -286,7 +286,7 @@ class AnnotationLoaderMixin:
|
|||||||
with open(ModuleName, 'r', encoding='utf-8') as f:
|
with open(ModuleName, 'r', encoding='utf-8') as f:
|
||||||
ann_content: str = f.read()
|
ann_content: str = f.read()
|
||||||
ann_content = ExpandSoftKeywords(ann_content)
|
ann_content = ExpandSoftKeywords(ann_content)
|
||||||
ann_tree: ast.Module = ast.parse(ann_content)
|
ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
|
||||||
for node in ann_tree.body:
|
for node in ann_tree.body:
|
||||||
# 检测 xxx: t.CTypedef = xxx 形式的 typedef 语句
|
# 检测 xxx: t.CTypedef = xxx 形式的 typedef 语句
|
||||||
if isinstance(node, ast.AnnAssign):
|
if isinstance(node, ast.AnnAssign):
|
||||||
|
|||||||
@@ -32,6 +32,19 @@ class ConstEvalMixin:
|
|||||||
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
|
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
|
||||||
if not isinstance(value_node, (ast.List, ast.Set)):
|
if not isinstance(value_node, (ast.List, ast.Set)):
|
||||||
return None
|
return None
|
||||||
|
# 显式空元素判据(与 TPV AssignGenMixin._init_global_array_from_list 的裸空指针
|
||||||
|
# 判据两侧一致):Python 侧的空 AST 元素就是 None(TPV 侧是裸 0)。列表元素计数
|
||||||
|
# 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值;
|
||||||
|
# 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。
|
||||||
|
for _gi, _elt in enumerate(value_node.elts):
|
||||||
|
if _elt is None:
|
||||||
|
raise TypeError(
|
||||||
|
f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。'
|
||||||
|
f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);'
|
||||||
|
f'拒绝静默补零生成残缺初始化,已终止编译。'
|
||||||
|
f'位置 line {getattr(value_node, "lineno", 0)}'
|
||||||
|
f':{getattr(value_node, "col_offset", 0)}'
|
||||||
|
)
|
||||||
if isinstance(ElemType, ir.ArrayType):
|
if isinstance(ElemType, ir.ArrayType):
|
||||||
constants: list[ir.Constant] = []
|
constants: list[ir.Constant] = []
|
||||||
for elt in value_node.elts:
|
for elt in value_node.elts:
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class LlvmGeneratorMixin:
|
|||||||
TypeInfo: CTypeInfo | None = None
|
TypeInfo: CTypeInfo | None = None
|
||||||
if Node.type_comment:
|
if Node.type_comment:
|
||||||
try:
|
try:
|
||||||
tc_ast: ast.Expression = ast.parse(Node.type_comment, mode='eval')
|
tc_ast: ast.Expression = ast.parse(Node.type_comment, '<LlvmGenerator:type_comment>', mode='eval')
|
||||||
TypeInfo = self.TypeMergeHandler.GetCTypeInfo(tc_ast.body)
|
TypeInfo = self.TypeMergeHandler.GetCTypeInfo(tc_ast.body)
|
||||||
except Exception as _e:
|
except Exception as _e:
|
||||||
if _config_mode == "strict":
|
if _config_mode == "strict":
|
||||||
@@ -701,10 +701,10 @@ class LlvmGeneratorMixin:
|
|||||||
# LlvmCodeGenerator;未启用时 dwarf_enabled=False → 不构造 DI、不挂 !dbg,
|
# LlvmCodeGenerator;未启用时 dwarf_enabled=False → 不构造 DI、不挂 !dbg,
|
||||||
# 生成的 .ll 与改动前逐字节一致(零影响)
|
# 生成的 .ll 与改动前逐字节一致(零影响)
|
||||||
self.LlvmGen.dwarf_enabled = bool(getattr(self, 'DwarfEnabled', False))
|
self.LlvmGen.dwarf_enabled = bool(getattr(self, 'DwarfEnabled', False))
|
||||||
# Ctrl+C 调用栈(--ctrlc-stack):透传"本模块需注入 Install() 启动期构造器"
|
# Ctrl+C 调用栈(--traceback):透传"本模块需注入 Install() 启动期构造器"
|
||||||
# 标记(仅 includes/dbgstack.vp 的翻译上下文为 True);未启用时为 False
|
# 标记(仅 includes/traceback.vp 的翻译上下文为 True);未启用时为 False
|
||||||
# → finalize() 不做任何事,生成的 .ll 与改动前逐字节一致(零影响)
|
# → finalize() 不做任何事,生成的 .ll 与改动前逐字节一致(零影响)
|
||||||
self.LlvmGen.ctrlc_stack_ctor = bool(getattr(self, 'CtrlcStackCtor', False))
|
self.LlvmGen.traceback_ctor = bool(getattr(self, 'TracebackCtor', False))
|
||||||
|
|
||||||
# --- 修复:共享 vtable 状态跨模块 ---
|
# --- 修复:共享 vtable 状态跨模块 ---
|
||||||
# 根因:LlvmGen 每模块重新创建,class_vtable/class_methods 等是实例级属性,
|
# 根因:LlvmGen 每模块重新创建,class_vtable/class_methods 等是实例级属性,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class PythonParserMixin:
|
|||||||
self.CurrentFile = FilePath
|
self.CurrentFile = FilePath
|
||||||
|
|
||||||
# 解析Python代码为AST
|
# 解析Python代码为AST
|
||||||
tree: ast.Module = ast.parse(content)
|
tree: ast.Module = ast.parse(content, FilePath)
|
||||||
|
|
||||||
# 保存 AST 树供后续使用
|
# 保存 AST 树供后续使用
|
||||||
self.tree = tree
|
self.tree = tree
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import threading
|
||||||
import vast as ast
|
import vast as ast
|
||||||
import warnings
|
import warnings
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
@@ -96,13 +97,68 @@ class TypeAnnotationResolver:
|
|||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# builtins 最后匹配(对齐 TPV TypeGenResolve._lookup_builtins_typedef):
|
# builtins 最后匹配(对齐 TPV TypeGenResolve._lookup_builtins_typedef):
|
||||||
# 所有常规注解解析路径 miss 后,扫描 includes/builtins.vp 的
|
# 所有常规注解解析路径 miss 后,扫描 includes/builtins.vp 的 typedef
|
||||||
# "NAME: t.CTypedef = TYPE" 声明作为最终兜底(rawptr/size_t 等)。
|
# 声明作为最终兜底(rawptr/sizet/uint64 等)。
|
||||||
# builtins 始终纳入编译链但不在 App 文件 import 闭包,其 typedef 不随
|
# builtins 始终纳入编译链但不在 App 文件 import 闭包,其 typedef 不随
|
||||||
# from_imports 注入,故需按源文件扫描兜底。
|
# from_imports 注入,故需按源文件扫描兜底。
|
||||||
|
# 必须同时兼容两种等价写法:
|
||||||
|
# - 语法糖 `typedef 名字 = 类型`(builtins.vp 现用形态)
|
||||||
|
# - 注解形态 `名字: t.CTypedef = 类型`(stdint.vp 等库模块)
|
||||||
|
# 只认注解形态时语法糖声明全被跳过 → 兜底表为空 → 裸名退化为
|
||||||
|
# `%"{mod}.名字"*` 假结构体(sizet 曾被当成结构体,BinOp 报
|
||||||
|
# "Operands must be the same type, got (%"…sizet"*, i64)")。
|
||||||
# ============================================================
|
# ============================================================
|
||||||
_builtins_scan_cache: dict | None = None
|
_builtins_scan_cache: dict | None = None
|
||||||
|
|
||||||
|
# builtins 解析链的**全链**重入深度上限与线程局部计数。
|
||||||
|
# 既有 `_resolve_builtins_type_str` 的 `depth`/`visited` 只在自身递归内传递;
|
||||||
|
# 而复合值(含 `|` / `[`)分支会经 `from_node` 重新进入
|
||||||
|
# `_from_node_name → _lookup_builtins_typedef`,把 depth/visited 归零 ⇒ 该方向
|
||||||
|
# 可绕过环检测无限递归(A = B | t.CPtr、B = A 一类声明)。此计数器覆盖
|
||||||
|
# `_lookup_builtins_typedef → _resolve_builtins_type_str → from_node → …` 全程,
|
||||||
|
# 超上限即判为成环并 fail-fast(不再沿链继续)。合法链通常 ≤ 4 层。
|
||||||
|
_BUILTINS_CHAIN_MAX_DEPTH: int = 32
|
||||||
|
_chain_state: threading.local = threading.local()
|
||||||
|
|
||||||
|
# 解析失败(成环/悬空)的 builtins typedef 名 → 错误报文缓存:
|
||||||
|
# 同一失败不再被反复重试(每次重试都会重新扫描/解析整条链)。
|
||||||
|
# builtins.vp 在一次编译进程内不变,故无需失效。
|
||||||
|
_builtins_failure_cache: dict[str, str] = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _chain_depth_enter(cls) -> int:
|
||||||
|
depth: int = getattr(cls._chain_state, 'depth', 0)
|
||||||
|
cls._chain_state.depth = depth + 1
|
||||||
|
return depth
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _chain_depth_exit(cls, prev: int) -> None:
|
||||||
|
cls._chain_state.depth = prev
|
||||||
|
|
||||||
|
# `typedef 名字 = 类型` 语法糖(对应 SymbolUtils._ExpandTypedefs 的展开形态)
|
||||||
|
_TYPEDEF_SUGAR_RE: re.Pattern = re.compile(
|
||||||
|
r'^typedef\s+([A-Za-z_]\w*)\s*=(?!=)\s*(.+?)\s*$')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parse_typedef_line(stripped: str) -> tuple[str, str] | None:
|
||||||
|
"""单行源码文本 → typedef 声明 (名字, 类型值);非 typedef 行返回 None
|
||||||
|
|
||||||
|
兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。
|
||||||
|
"""
|
||||||
|
m: re.Match | None = TypeAnnotationResolver._TYPEDEF_SUGAR_RE.match(stripped)
|
||||||
|
if m is not None:
|
||||||
|
return m.group(1), m.group(2)
|
||||||
|
if ':' not in stripped or 't.CTypedef' not in stripped:
|
||||||
|
return None
|
||||||
|
name, _, rest = stripped.partition(':')
|
||||||
|
if 't.CTypedef' not in rest:
|
||||||
|
return None
|
||||||
|
value: str = rest.partition('=')[2].strip()
|
||||||
|
name = name.strip()
|
||||||
|
if not name or not value:
|
||||||
|
return None
|
||||||
|
return name, value
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _builtins_include_dirs() -> list[str]:
|
def _builtins_include_dirs() -> list[str]:
|
||||||
"""includes 目录列表(builtins.vp 定位用)——只认项目配置 includes,
|
"""includes 目录列表(builtins.vp 定位用)——只认项目配置 includes,
|
||||||
@@ -150,23 +206,23 @@ class TypeAnnotationResolver:
|
|||||||
if m:
|
if m:
|
||||||
aliases[m.group(2)] = m.group(1)
|
aliases[m.group(2)] = m.group(1)
|
||||||
continue
|
continue
|
||||||
if ':' not in stripped:
|
td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped)
|
||||||
continue
|
if td is not None:
|
||||||
name: str
|
typedefs[td[0]] = td[1]
|
||||||
rest: str
|
# 只缓存"扫到了东西"的结果:includes 目录尚未装配(PROJECT_INCLUDE_DIRS 为空)
|
||||||
name, _, rest = stripped.partition(':')
|
# 或 builtins.vp 未定位时,缓存空表会让本进程内后续所有 builtins typedef 解析
|
||||||
name = name.strip()
|
# (sizet(x)/uint64(x) 这类 cast 调用)永久 miss,只在真正扫到内容后落缓存。
|
||||||
if 't.CTypedef' not in rest:
|
if not typedefs and not aliases:
|
||||||
continue
|
return {'typedefs': typedefs, 'aliases': aliases}
|
||||||
value: str = rest.partition('=')[2].strip()
|
|
||||||
if value:
|
|
||||||
typedefs[name] = value
|
|
||||||
cls._builtins_scan_cache = {'typedefs': typedefs, 'aliases': aliases}
|
cls._builtins_scan_cache = {'typedefs': typedefs, 'aliases': aliases}
|
||||||
return cls._builtins_scan_cache
|
return cls._builtins_scan_cache
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _scan_library_typedef(cls, mod: str, attr: str) -> str | None:
|
def _scan_library_typedef(cls, mod: str, attr: str) -> str | None:
|
||||||
"""查库模块源中 "attr: t.CTypedef = TYPE",返回 TYPE(权威定义)或 None"""
|
"""查库模块源中 typedef 声明,返回类型值(权威定义)或 None
|
||||||
|
|
||||||
|
兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。
|
||||||
|
"""
|
||||||
path: str | None = TypeAnnotationResolver._find_include_file(mod)
|
path: str | None = TypeAnnotationResolver._find_include_file(mod)
|
||||||
if path is None:
|
if path is None:
|
||||||
return None
|
return None
|
||||||
@@ -180,29 +236,24 @@ class TypeAnnotationResolver:
|
|||||||
stripped: str = line.strip()
|
stripped: str = line.strip()
|
||||||
if not stripped or stripped.startswith('#'):
|
if not stripped or stripped.startswith('#'):
|
||||||
continue
|
continue
|
||||||
if ':' not in stripped:
|
td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped)
|
||||||
continue
|
if td is not None and td[0] == attr:
|
||||||
name: str
|
return td[1]
|
||||||
rest: str
|
|
||||||
name, _, rest = stripped.partition(':')
|
|
||||||
if name.strip() != attr:
|
|
||||||
continue
|
|
||||||
if 't.CTypedef' not in rest:
|
|
||||||
continue
|
|
||||||
value: str = rest.partition('=')[2].strip()
|
|
||||||
if value:
|
|
||||||
return value
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _resolve_builtins_type_str(cls, type_str: str, aliases: dict[str, str],
|
def _resolve_builtins_type_str(cls, type_str: str, aliases: dict[str, str],
|
||||||
SymbolTable, depth: int = 0) -> CTypeInfo | None:
|
SymbolTable, depth: int = 0,
|
||||||
|
visited: frozenset[str] | None = None) -> CTypeInfo | None:
|
||||||
"""解析 builtins typedef 值(支持 t.XXX / mod.attr / 裸名;mod 支持 import 别名)
|
"""解析 builtins typedef 值(支持 t.XXX / mod.attr / 裸名;mod 支持 import 别名)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
type_str: typedef 值(如 t.CPtr / __stdint.size_t / CSizeT)
|
type_str: typedef 值(如 t.CPtr / __stdint.size_t / CSizeT / sizet)
|
||||||
aliases: builtins.vp 的 import 别名表(__stdint → stdint)
|
aliases: builtins.vp 的 import 别名表(__stdint → stdint)
|
||||||
depth: 递归深度守卫(防 typedef 值自引用成环)
|
depth: 递归深度守卫(防 typedef 值自引用成环)
|
||||||
|
visited: 本次展开链上已展开的 typedef 名(环检测)。命中环/自引用返回
|
||||||
|
None(而不是退化成 %"{mod}.{名}"* 假结构体),由调用方
|
||||||
|
_lookup_builtins_typedef fail-fast 报出根因。
|
||||||
"""
|
"""
|
||||||
if not type_str or depth > 8:
|
if not type_str or depth > 8:
|
||||||
return None
|
return None
|
||||||
@@ -225,8 +276,13 @@ class TypeAnnotationResolver:
|
|||||||
mod = mod.strip()
|
mod = mod.strip()
|
||||||
attr = attr.strip()
|
attr = attr.strip()
|
||||||
real_mod: str = aliases.get(mod, mod)
|
real_mod: str = aliases.get(mod, mod)
|
||||||
# t/c 内建命名空间:走常规 Attribute 解析(含 CPtr/State 特殊语义)
|
# t/c 内建命名空间:**优先按内建类名直接解析**,不依赖调用方 SymbolTable
|
||||||
|
# 的装配进度(import 注册阶段 t 尚未入表 ⇒ 原先返回 None,被上层误归因为
|
||||||
|
# "自引用/循环引用";实测 builtins typedef 'sizet' → 't.CSizeT' 即卡在此处)。
|
||||||
if real_mod in ('t', 'c'):
|
if real_mod in ('t', 'c'):
|
||||||
|
_bi: CTypeInfo | None = TypeAnnotationResolver.from_type_name(attr)
|
||||||
|
if _bi is not None:
|
||||||
|
return _bi
|
||||||
node = TypeAnnotationResolver._parse_type_expr(type_str)
|
node = TypeAnnotationResolver._parse_type_expr(type_str)
|
||||||
if node is not None:
|
if node is not None:
|
||||||
return TypeAnnotationResolver.from_node(node, SymbolTable)
|
return TypeAnnotationResolver.from_node(node, SymbolTable)
|
||||||
@@ -234,20 +290,58 @@ class TypeAnnotationResolver:
|
|||||||
# 库模块权威 typedef(stdint.size_t → 读 stdint.vp 扫描)
|
# 库模块权威 typedef(stdint.size_t → 读 stdint.vp 扫描)
|
||||||
lib_val: str | None = TypeAnnotationResolver._scan_library_typedef(real_mod, attr)
|
lib_val: str | None = TypeAnnotationResolver._scan_library_typedef(real_mod, attr)
|
||||||
if lib_val is not None:
|
if lib_val is not None:
|
||||||
return TypeAnnotationResolver._resolve_builtins_type_str(lib_val, aliases, SymbolTable, depth + 1)
|
return TypeAnnotationResolver._resolve_builtins_type_str(
|
||||||
|
lib_val, aliases, SymbolTable, depth + 1, visited)
|
||||||
# 库源未命中:回退常规解析(模块可能已由 import 闭包注入 SymbolTable)
|
# 库源未命中:回退常规解析(模块可能已由 import 闭包注入 SymbolTable)
|
||||||
node = TypeAnnotationResolver._parse_type_expr(type_str)
|
node = TypeAnnotationResolver._parse_type_expr(type_str)
|
||||||
if node is not None:
|
if node is not None:
|
||||||
return TypeAnnotationResolver.from_node(node, SymbolTable)
|
return TypeAnnotationResolver.from_node(node, SymbolTable)
|
||||||
return None
|
return None
|
||||||
# 裸名:按 BuiltinTypeMap/t 类名解析
|
# 裸名:名字本身即类型(t/C 类名、BuiltinTypeMap 的 C 速记名、裸 LLVM 标量名)
|
||||||
return TypeAnnotationResolver.from_type_name(type_str)
|
# → 直接解析;否则它可能是**另一个 typedef 的引用**,沿 builtins typedef 表
|
||||||
|
# 继续展开到基础类型。
|
||||||
|
# 依据:stdint.vp 的 `size_t/SIZE_T: t.CTypedef = sizet` 与 builtins.vp 的
|
||||||
|
# `size_t/uzt: t.CTypedef = sizet` 都是"别名 → 权威名 → t 类型"的链式写法,
|
||||||
|
# 与 TPV resolve_typedef_or_map 的 CTypedef 表递归(TypeGenResolve.vp:723-734)
|
||||||
|
# 同口径。此前裸名一律走 from_type_name:链中名字命中不到基础类型时退化成
|
||||||
|
# %"{mod}.{名}"* 假结构体(sizet/size_t → "Type of #N arg mismatch:
|
||||||
|
# %…sizet"* != i32" 的根因)。
|
||||||
|
if TypeAnnotationResolver._is_direct_type_name(type_str):
|
||||||
|
return TypeAnnotationResolver.from_type_name(type_str)
|
||||||
|
chain: str | None = TypeAnnotationResolver._scan_builtins()['typedefs'].get(type_str)
|
||||||
|
if chain is None:
|
||||||
|
# 非 builtins typedef 名的裸名(str/bytes/None 等):保留既有兜底
|
||||||
|
return TypeAnnotationResolver.from_type_name(type_str)
|
||||||
|
seen: frozenset[str] = visited or frozenset()
|
||||||
|
if type_str in seen or chain == type_str:
|
||||||
|
# 自引用/环(如 `sizet: t.CTypedef = sizet`):无信息可展开,返回 None
|
||||||
|
return None
|
||||||
|
return TypeAnnotationResolver._resolve_builtins_type_str(
|
||||||
|
chain, aliases, SymbolTable, depth + 1, seen | {type_str})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_direct_type_name(name: str) -> bool:
|
||||||
|
"""名字本身即类型(非"指向另一个 typedef 的别名")
|
||||||
|
|
||||||
|
覆盖 t 的 CType 子类名(CInt/CChar/CSizeT/CInt64T 等)、BuiltinTypeMap 的
|
||||||
|
C 速记名(int/char/bool)与裸 LLVM 标量名(i64/double…,泛型推导产物)。
|
||||||
|
"""
|
||||||
|
if not name:
|
||||||
|
return False
|
||||||
|
if BuiltinTypeMap.Get(name) is not None:
|
||||||
|
return True
|
||||||
|
if name in CTypeInfo._get_llvm_primitive_map():
|
||||||
|
return True
|
||||||
|
cls: type | None = CTypeHelper.GetTModuleCType(name)
|
||||||
|
if cls is None:
|
||||||
|
cls = getattr(t, name, None)
|
||||||
|
return isinstance(cls, type) and issubclass(cls, t.CType) and cls is not t.CType
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_type_expr(text: str) -> ast.AST | None:
|
def _parse_type_expr(text: str) -> ast.AST | None:
|
||||||
"""vast mode='eval' 解析单个表达式,返回表达式节点或 None"""
|
"""vast mode='eval' 解析单个表达式,返回表达式节点或 None"""
|
||||||
try:
|
try:
|
||||||
expr_tree: ast.AST = ast.parse(text, mode='eval')
|
expr_tree: ast.AST = ast.parse(text, '<TypeAnnotationResolver:type_expr>', mode='eval')
|
||||||
body = getattr(expr_tree, 'body', None)
|
body = getattr(expr_tree, 'body', None)
|
||||||
if body is not None and not isinstance(body, list):
|
if body is not None and not isinstance(body, list):
|
||||||
return body
|
return body
|
||||||
@@ -278,18 +372,52 @@ class TypeAnnotationResolver:
|
|||||||
"""builtins 最后匹配:查 includes/builtins.vp 的 CTypedef 声明
|
"""builtins 最后匹配:查 includes/builtins.vp 的 CTypedef 声明
|
||||||
|
|
||||||
命中(该名字确为 builtins typedef)返回解析出的 CTypeInfo,
|
命中(该名字确为 builtins typedef)返回解析出的 CTypeInfo,
|
||||||
miss 返回 None(交由调用方走原有兜底)。
|
miss(该名字不是 builtins typedef)返回 None(交由调用方走原有兜底)。
|
||||||
rawptr 为编译器内置语法:优先于一切外部声明(不可重定义)。
|
rawptr 为编译器内置语法:优先于一切外部声明(不可重定义)。
|
||||||
|
名字在表内却解析不出基础类型 = 声明本身无信息/成环(自引用)⇒ fail-fast,
|
||||||
|
不再静默退化为 %"{mod}.{名}"* 假结构体(下游仅报 "Type of #N arg
|
||||||
|
mismatch: %…* != …",无法归因到 typedef 声明)。
|
||||||
"""
|
"""
|
||||||
if not TypeName:
|
if not TypeName:
|
||||||
return None
|
return None
|
||||||
if TypeName == cls._BUILTIN_ROWPTR_NAME:
|
if TypeName == cls._BUILTIN_ROWPTR_NAME:
|
||||||
return cls._builtin_rawptr_info()
|
return cls._builtin_rawptr_info()
|
||||||
data: dict = cls._scan_builtins()
|
# 同一失败不再反复解析:命中失败缓存则直接按既有报文 fail-fast,
|
||||||
type_str: str | None = data['typedefs'].get(TypeName)
|
# 不再重扫 builtins.vp / 重走整条解析链。
|
||||||
if type_str is None:
|
_cached_err: str | None = cls._builtins_failure_cache.get(TypeName)
|
||||||
return None
|
if _cached_err is not None:
|
||||||
return cls._resolve_builtins_type_str(type_str, data['aliases'], SymbolTable)
|
raise ValueError(_cached_err)
|
||||||
|
# 全链重入深度守卫:覆盖 `_lookup_builtins_typedef → _resolve_builtins_type_str
|
||||||
|
# → from_node → _from_node_name → _lookup_builtins_typedef` 的所有回路
|
||||||
|
# (含 `|`/`[` 复合值分支导致的 depth/visited 归零绕过)。超上限即成环。
|
||||||
|
_prev_depth: int = cls._chain_depth_enter()
|
||||||
|
try:
|
||||||
|
if _prev_depth >= cls._BUILTINS_CHAIN_MAX_DEPTH:
|
||||||
|
_err: str = (
|
||||||
|
f"builtins typedef 解析链成环或过深(>{cls._BUILTINS_CHAIN_MAX_DEPTH} 层),"
|
||||||
|
f"解析 '{TypeName}' 时终止:includes/builtins.vp 中相关 typedef 声明"
|
||||||
|
f"(含 `|` / `[...]` 复合值)互相引用成环。请改为指向真实基础类型"
|
||||||
|
f"(如 t.CSizeT),或断开环上引用。")
|
||||||
|
cls._builtins_failure_cache[TypeName] = _err
|
||||||
|
raise ValueError(_err)
|
||||||
|
data: dict = cls._scan_builtins()
|
||||||
|
type_str: str | None = data['typedefs'].get(TypeName)
|
||||||
|
if type_str is None:
|
||||||
|
return None
|
||||||
|
info: CTypeInfo | None = cls._resolve_builtins_type_str(type_str, data['aliases'], SymbolTable)
|
||||||
|
if info is None:
|
||||||
|
# 不再一律归因"自引用/循环引用"(那正是把 t.CSizeT 解析失败误报成环、
|
||||||
|
# 把人引向错误方向的根因):把声明值与该值解析失败的形态如实报出。
|
||||||
|
_err = (
|
||||||
|
f"builtins typedef '{TypeName}' 无法解析到基础类型:includes/builtins.vp "
|
||||||
|
f"声明 '{TypeName}: t.CTypedef = {type_str}',其值 '{type_str}' 解析失败"
|
||||||
|
f"(既不是内建类型名,也不是链上可展开的 typedef)。"
|
||||||
|
f"请把该声明指向真实类型(如 t.CSizeT),或确认该类型名存在。")
|
||||||
|
cls._builtins_failure_cache[TypeName] = _err
|
||||||
|
raise ValueError(_err)
|
||||||
|
return info
|
||||||
|
finally:
|
||||||
|
cls._chain_depth_exit(_prev_depth)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _from_node_constant(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo:
|
def _from_node_constant(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo:
|
||||||
@@ -742,6 +870,17 @@ class TypeAnnotationResolver:
|
|||||||
Result.ArrayDims.insert(0, str(count_val))
|
Result.ArrayDims.insert(0, str(count_val))
|
||||||
else:
|
else:
|
||||||
Result.PtrCount += 1
|
Result.PtrCount += 1
|
||||||
|
else:
|
||||||
|
# 单参数 t.CArray[T](= t.CArray[T, None]):长度自动推导数组。
|
||||||
|
# 指针位置语义为"元素指针" Ptr(T),需在元素类型上再加一层指针。
|
||||||
|
# 与裸名 CArray[T](HandlesTypeMerge.CollectAll 的
|
||||||
|
# `ElementType.PtrCount + 1`)、stub 侧(LLVMTypeMapper
|
||||||
|
# IsListAnnotation 的 `elem_type + '*'`)、声明端
|
||||||
|
# (DeclarationGenerator `[0 x T]` → _decay_array_to_ptr = T*)
|
||||||
|
# 以及 TPV(TypeGenResolve 单参 → Ptr(elem_ty))同口径。
|
||||||
|
# 此前缺这一层:t.CArray[str] 得 i8*(即 str 本身),
|
||||||
|
# result[k] 退化成 i8 → 写字符串指针触发 ptr→int fail-fast。
|
||||||
|
Result.PtrCount += 1
|
||||||
if ElemInfo.IsPtr and not ElemInfo.ArrayDims:
|
if ElemInfo.IsPtr and not ElemInfo.ArrayDims:
|
||||||
Result.PtrCount = max(Result.PtrCount, 1)
|
Result.PtrCount = max(Result.PtrCount, 1)
|
||||||
return Result
|
return Result
|
||||||
@@ -789,12 +928,21 @@ class TypeAnnotationResolver:
|
|||||||
params_list = slice_node.elts[0]
|
params_list = slice_node.elts[0]
|
||||||
return_node = slice_node.elts[1]
|
return_node = slice_node.elts[1]
|
||||||
ParamTypes = []
|
ParamTypes = []
|
||||||
if isinstance(params_list, ast.List):
|
_ParamsIsList: bool = isinstance(params_list, ast.List)
|
||||||
|
if _ParamsIsList:
|
||||||
for elt in params_list.elts:
|
for elt in params_list.elts:
|
||||||
ParamTypeInfo = TypeAnnotationResolver.from_node(elt, SymbolTable)
|
ParamTypeInfo = TypeAnnotationResolver.from_node(elt, SymbolTable)
|
||||||
if ParamTypeInfo:
|
if ParamTypeInfo:
|
||||||
ParamTypes.append(('', ParamTypeInfo))
|
ParamTypes.append(('', ParamTypeInfo))
|
||||||
if not ParamTypes:
|
# 空参数表 `t.Callable[[], R]` 语义 = 零形参:LLVM 端即 `R ()*`,
|
||||||
|
# 调用点按零实参间接 call。此前在此注入 void 占位形参 → 得
|
||||||
|
# `R (void)*`(void 作形参本身不是合法 LLVM 函数类型),调用点
|
||||||
|
# 实参个数(0) 与形参个数(1) 不符 → llvmlite CallInstr
|
||||||
|
# `arg = args[i]` IndexError(Repl.vp 模块级 t.Callable 槽
|
||||||
|
# `ReplFragFn()` 翻译崩溃根因);与本文件另三处 Callable 形态
|
||||||
|
# (:378 / :422 / :471)的 `FuncPtrParams = []` 口径统一。
|
||||||
|
# 非 List 形态(遗留写法 t.Callable[A, R])保留原 void 占位行为。
|
||||||
|
if not ParamTypes and not _ParamsIsList:
|
||||||
ParamTypes.append(('', TypeAnnotationResolver.from_type_name('void')))
|
ParamTypes.append(('', TypeAnnotationResolver.from_type_name('void')))
|
||||||
Result.FuncPtrParams = ParamTypes
|
Result.FuncPtrParams = ParamTypes
|
||||||
ReturnTypeInfo = TypeAnnotationResolver.from_node(return_node, SymbolTable)
|
ReturnTypeInfo = TypeAnnotationResolver.from_node(return_node, SymbolTable)
|
||||||
|
|||||||
+248
-19
@@ -38,6 +38,32 @@ def _JoinConstParts(parts: list[object]) -> str | bytes:
|
|||||||
return ''.join(str(p) for p in parts)
|
return ''.join(str(p) for p in parts)
|
||||||
|
|
||||||
|
|
||||||
|
_HEX_ALPHABET: str = '0123456789abcdefABCDEF'
|
||||||
|
|
||||||
|
|
||||||
|
def _HexValOf(ch: str) -> int:
|
||||||
|
"""单个字符的十六进制位值;非十六进制字符返回 0(与 C 解析器 vast_c.HexVal 同口径)。"""
|
||||||
|
idx: int = _HEX_ALPHABET.find(ch)
|
||||||
|
if idx < 0:
|
||||||
|
return 0
|
||||||
|
return idx if idx < 16 else idx - 6
|
||||||
|
|
||||||
|
|
||||||
|
def _HexEscapeVal(body: str, start: int, count: int) -> int:
|
||||||
|
"""取转义序列后 count 位十六进制的值(越界位置与非十六进制字符均按 0 计)。
|
||||||
|
|
||||||
|
两侧解析器对非法 `\\x` / `\\u` / `\\U` 转义必须产出同一结果且都不许抛异常:
|
||||||
|
C 解析器走 HexVal(非 hex → 0),此处原来直接 `int(..., 16)`,遇 `\\xNN`
|
||||||
|
这类字面量会抛 ValueError(includes/ast/lexer.vp 的文档字符串即触发)。
|
||||||
|
"""
|
||||||
|
val: int = 0
|
||||||
|
n: int = len(body)
|
||||||
|
for k in range(count):
|
||||||
|
idx: int = start + k
|
||||||
|
val = val * 16 + _HexValOf(body[idx] if idx < n else '0')
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
class TokenStream:
|
class TokenStream:
|
||||||
"""token 流管理基类:统一提供游标操作、位置标注工具。"""
|
"""token 流管理基类:统一提供游标操作、位置标注工具。"""
|
||||||
|
|
||||||
@@ -54,6 +80,13 @@ class TokenStream:
|
|||||||
# 类型注解解析模式:为 True 时 '*' / '**' 按 C 风格指针后缀处理
|
# 类型注解解析模式:为 True 时 '*' / '**' 按 C 风格指针后缀处理
|
||||||
# (int* → int | t.CPtr、int** → 嵌套两层),不按乘法/幂运算消费。
|
# (int* → int | t.CPtr、int** → 嵌套两层),不按乘法/幂运算消费。
|
||||||
self._InTypeAnnotation: bool = False
|
self._InTypeAnnotation: bool = False
|
||||||
|
# 下标实参嵌套层数:'[' 实参内是表达式位置(t.CArray[T, A * B] 的尺寸),
|
||||||
|
# 此处的 '*' 在有右操作数时按乘法解析(见 _ParseTerm / _AtOperandAfterStarRun)。
|
||||||
|
self._SubscriptArgs: int = 0
|
||||||
|
# Tail token of the last consumed C-style pointer suffix ('*' / '**').
|
||||||
|
# _ParseParen compares it against _Prev() to recognize the paren-less cast
|
||||||
|
# `(T*)x`, which must not be mistaken for a plain group `(a | b)`.
|
||||||
|
self._PtrSuffixTail: Token | None = None
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# 游标操作
|
# 游标操作
|
||||||
@@ -397,8 +430,17 @@ class ExprParserMixin(TokenStream):
|
|||||||
tok: Token = self._Peek()
|
tok: Token = self._Peek()
|
||||||
op_type: type | None = None
|
op_type: type | None = None
|
||||||
if tok.Type == OP:
|
if tok.Type == OP:
|
||||||
|
if tok.Value == '*' and self._AtPtrSuffix(left):
|
||||||
|
# C 风格指针后缀('*' 一层);展开为 base | t.CPtr 后继续。
|
||||||
|
left = self._ConsumePtrSuffix(left)
|
||||||
|
continue
|
||||||
if self._InTypeAnnotation and tok.Value == '*':
|
if self._InTypeAnnotation and tok.Value == '*':
|
||||||
break # 注解模式:'*' 是 C 风格指针后缀标记,不按乘法处理
|
# 注解模式:'*' 后已无操作数(']' / ',' / ')' / ':' / '=' / 行尾 / ';')
|
||||||
|
# 时它是 C 风格指针后缀标记,交上方判据与 _ParseTypeAnnotation 统一消费;
|
||||||
|
# 但下标实参内的尺寸表达式(t.CArray[T, A * B])'*' 后仍有操作数,
|
||||||
|
# 必须按乘法继续——否则下标收尾报 `expected ']'`。
|
||||||
|
if not (self._SubscriptArgs > 0 and self._AtOperandAfterStarRun()):
|
||||||
|
break
|
||||||
op_type = {'*': N.Mult, '/': N.Div, '//': N.FloorDiv,
|
op_type = {'*': N.Mult, '/': N.Div, '//': N.FloorDiv,
|
||||||
'%': N.Mod, '@': N.MatMult}.get(tok.Value)
|
'%': N.Mod, '@': N.MatMult}.get(tok.Value)
|
||||||
if op_type is None:
|
if op_type is None:
|
||||||
@@ -429,6 +471,9 @@ class ExprParserMixin(TokenStream):
|
|||||||
start: Token = self._Peek()
|
start: Token = self._Peek()
|
||||||
base: N.expr = self._ParseAwaitPrimary()
|
base: N.expr = self._ParseAwaitPrimary()
|
||||||
if self._AtOp('**'):
|
if self._AtOp('**'):
|
||||||
|
if self._AtPtrSuffix(base):
|
||||||
|
# C 风格指针后缀('**' 计 2 层)
|
||||||
|
return self._ConsumePtrSuffix(base)
|
||||||
if self._InTypeAnnotation:
|
if self._InTypeAnnotation:
|
||||||
return base # 注解模式:'**' 是 C 风格指针后缀标记(2 层),不按幂处理
|
return base # 注解模式:'**' 是 C 风格指针后缀标记(2 层),不按幂处理
|
||||||
self._Next()
|
self._Next()
|
||||||
@@ -456,7 +501,13 @@ class ExprParserMixin(TokenStream):
|
|||||||
while True:
|
while True:
|
||||||
self._SkipNL()
|
self._SkipNL()
|
||||||
tok: Token = self._Peek()
|
tok: Token = self._Peek()
|
||||||
if tok.Type == OP and tok.Value == '.':
|
if tok.Type == OP and tok.Value in ('*', '**') and self._AtPtrSuffixCall(node):
|
||||||
|
# `T*(x)` / `T**(x)` 指针后缀强转:解析为 cast 调用(与
|
||||||
|
# `(T | t.CPtr)(x)` 同形)。在 trailer 循环里消费,故其后的
|
||||||
|
# `.attr` / `[i]` / `(…)` 仍由本循环继续挂接(如
|
||||||
|
# `ast.Attribute*(expr_root).value`)。
|
||||||
|
node = self._ConsumePtrSuffixCall(node, start)
|
||||||
|
elif tok.Type == OP and tok.Value == '.':
|
||||||
self._Next()
|
self._Next()
|
||||||
attr_tok: Token = self._ExpectName()
|
attr_tok: Token = self._ExpectName()
|
||||||
attr_node: N.Attribute = N.Attribute(value=node, attr=attr_tok.Value, ctx=N.Load())
|
attr_node: N.Attribute = N.Attribute(value=node, attr=attr_tok.Value, ctx=N.Load())
|
||||||
@@ -543,13 +594,22 @@ class ExprParserMixin(TokenStream):
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
def _ParseSliceList(self) -> N.AST:
|
def _ParseSliceList(self) -> N.AST:
|
||||||
"""解析下标列表:a[1] / a[1:2] / a[1:2:3] / a[1,2] / a[...] / a[::]"""
|
"""解析下标列表:a[1] / a[1:2] / a[1:2:3] / a[1,2] / a[...] / a[::]
|
||||||
start: Token = self._Peek()
|
|
||||||
elts: list[N.AST] = [self._ParseSliceItem()]
|
下标实参内是**表达式**位置(如 `t.CArray[T, A * B]` 的尺寸),此处的 '*'
|
||||||
while self._AtOp(','):
|
应按乘法解析;同时 `t.CArray[str*, 4]` 的指针后缀仍要成立——两者由
|
||||||
self._Next()
|
`_AtOperandAfterStarRun`('*' 后是否还有操作数)区分,故此处登记已进入实参。
|
||||||
self._SkipNL()
|
"""
|
||||||
elts.append(self._ParseSliceItem())
|
self._SubscriptArgs += 1
|
||||||
|
try:
|
||||||
|
start: Token = self._Peek()
|
||||||
|
elts: list[N.AST] = [self._ParseSliceItem()]
|
||||||
|
while self._AtOp(','):
|
||||||
|
self._Next()
|
||||||
|
self._SkipNL()
|
||||||
|
elts.append(self._ParseSliceItem())
|
||||||
|
finally:
|
||||||
|
self._SubscriptArgs -= 1
|
||||||
if len(elts) == 1:
|
if len(elts) == 1:
|
||||||
return elts[0]
|
return elts[0]
|
||||||
tup: N.Tuple = N.Tuple(elts=elts, ctx=N.Load())
|
tup: N.Tuple = N.Tuple(elts=elts, ctx=N.Load())
|
||||||
@@ -770,16 +830,13 @@ class ExprParserMixin(TokenStream):
|
|||||||
out.append(simple[esc])
|
out.append(simple[esc])
|
||||||
i += 1
|
i += 1
|
||||||
elif esc == 'x':
|
elif esc == 'x':
|
||||||
hex_digits: str = body[i + 1:i + 3]
|
out.append(chr(_HexEscapeVal(body, i + 1, 2)))
|
||||||
out.append(chr(int(hex_digits, 16)))
|
|
||||||
i += 3
|
i += 3
|
||||||
elif esc == 'u':
|
elif esc == 'u':
|
||||||
hex_digits = body[i + 1:i + 5]
|
out.append(chr(_HexEscapeVal(body, i + 1, 4)))
|
||||||
out.append(chr(int(hex_digits, 16)))
|
|
||||||
i += 5
|
i += 5
|
||||||
elif esc == 'U':
|
elif esc == 'U':
|
||||||
hex_digits = body[i + 1:i + 9]
|
out.append(chr(_HexEscapeVal(body, i + 1, 8)))
|
||||||
out.append(chr(int(hex_digits, 16)))
|
|
||||||
i += 9
|
i += 9
|
||||||
elif esc.isdigit() and '0' <= esc <= '7':
|
elif esc.isdigit() and '0' <= esc <= '7':
|
||||||
oct_digits: str = body[i:i + 3]
|
oct_digits: str = body[i:i + 3]
|
||||||
@@ -823,13 +880,13 @@ class ExprParserMixin(TokenStream):
|
|||||||
out.append(simple[esc])
|
out.append(simple[esc])
|
||||||
i += 1
|
i += 1
|
||||||
elif esc == 'x':
|
elif esc == 'x':
|
||||||
out.append(int(body[i + 1:i + 3], 16))
|
out.append(_HexEscapeVal(body, i + 1, 2))
|
||||||
i += 3
|
i += 3
|
||||||
elif esc == 'u':
|
elif esc == 'u':
|
||||||
out.append(int(body[i + 1:i + 5], 16))
|
out.append(_HexEscapeVal(body, i + 1, 4))
|
||||||
i += 5
|
i += 5
|
||||||
elif esc == 'U':
|
elif esc == 'U':
|
||||||
out.append(int(body[i + 1:i + 9], 16))
|
out.append(_HexEscapeVal(body, i + 1, 8))
|
||||||
i += 9
|
i += 9
|
||||||
elif esc.isdigit() and '0' <= esc <= '7':
|
elif esc.isdigit() and '0' <= esc <= '7':
|
||||||
oct_digits: str = body[i:i + 3]
|
oct_digits: str = body[i:i + 3]
|
||||||
@@ -1105,6 +1162,10 @@ class ExprParserMixin(TokenStream):
|
|||||||
self._FixLoc(walrus, start)
|
self._FixLoc(walrus, start)
|
||||||
return walrus
|
return walrus
|
||||||
first: N.expr = self.ParseExpr()
|
first: N.expr = self.ParseExpr()
|
||||||
|
# Paren-less C cast `(T*)x`: the inner expression ends with a pointer suffix
|
||||||
|
# (_Prev() is that suffix's tail token) and the operand follows ')'. Same
|
||||||
|
# product as `(T*)(x)` / `T*(x)`: a Call whose func is `T | t.CPtr`.
|
||||||
|
is_ptr_cast: bool = self._PtrSuffixTail is not None and self._AtOp(')') and self._Prev() is self._PtrSuffixTail
|
||||||
self._SkipNL()
|
self._SkipNL()
|
||||||
# 生成器表达式 (x for ...)
|
# 生成器表达式 (x for ...)
|
||||||
if self._AtName('for'):
|
if self._AtName('for'):
|
||||||
@@ -1130,6 +1191,11 @@ class ExprParserMixin(TokenStream):
|
|||||||
return tup
|
return tup
|
||||||
# 普通括号表达式 (x)
|
# 普通括号表达式 (x)
|
||||||
self._ExpectOp(')')
|
self._ExpectOp(')')
|
||||||
|
if is_ptr_cast and self._AtCastOperand():
|
||||||
|
operand: N.expr = self._ParseFactor()
|
||||||
|
cast_call: N.Call = N.Call(func=first, args=[operand], keywords=[])
|
||||||
|
self._FixLoc(cast_call, start)
|
||||||
|
return cast_call
|
||||||
return first
|
return first
|
||||||
|
|
||||||
def _ParseList(self) -> N.expr:
|
def _ParseList(self) -> N.expr:
|
||||||
@@ -1496,6 +1562,11 @@ class ExprParserMixin(TokenStream):
|
|||||||
展开为 BinOp(BitOr) 链后,TPC/TPV 现有类型解析(TypeAnnotationResolver
|
展开为 BinOp(BitOr) 链后,TPC/TPV 现有类型解析(TypeAnnotationResolver
|
||||||
的 PtrCount 相加、TPV TypeGenResolve 的递归 BinOp 分支)零改动即可
|
的 PtrCount 相加、TPV TypeGenResolve 的递归 BinOp 分支)零改动即可
|
||||||
生成多级指针 LLVM 类型。
|
生成多级指针 LLVM 类型。
|
||||||
|
|
||||||
|
无法解析的后缀形态**响亮失败**(SyntaxError,含 行:列 + 源码片段 + 形态 +
|
||||||
|
正确写法示例),绝不静默丢弃:注解末尾若仍遗留未被 `_AtPtrSuffix` 接受的
|
||||||
|
'*'/'**'(如 `a: int * 2` / `a: int* | t.CPtr`),判据拒绝消费即终止编译
|
||||||
|
(历史事故:静默退化会产出错误的指针层数)。
|
||||||
"""
|
"""
|
||||||
saved_flag: bool = self._InTypeAnnotation
|
saved_flag: bool = self._InTypeAnnotation
|
||||||
self._InTypeAnnotation = True
|
self._InTypeAnnotation = True
|
||||||
@@ -1505,20 +1576,178 @@ class ExprParserMixin(TokenStream):
|
|||||||
self._InTypeAnnotation = saved_flag
|
self._InTypeAnnotation = saved_flag
|
||||||
if expr is None:
|
if expr is None:
|
||||||
return None
|
return None
|
||||||
# 循环消费 '*' / '**' 后缀('**' 是词法单 token,计 2 层)
|
if self._AtPtrSuffix(expr):
|
||||||
|
return self._ConsumePtrSuffix(expr)
|
||||||
|
tok: Token = self._Peek()
|
||||||
|
if tok.Type == OP and tok.Value in ('*', '**'):
|
||||||
|
snippet: str = ''
|
||||||
|
try:
|
||||||
|
snippet = (self.Source.splitlines()[tok.Line - 1] or '').strip()
|
||||||
|
except Exception:
|
||||||
|
snippet = ''
|
||||||
|
raise SyntaxError(
|
||||||
|
f"[vast] 注解位置的指针后缀无法解析:第 {tok.Line} 行第 {tok.Col} 列的 "
|
||||||
|
f"{tok.Value!r} 前面不是可加后缀的类型(Name / Attribute / Subscript),"
|
||||||
|
f"或其后不是后缀的合法结束位置(注解结尾,或 ']' / ',' / ')' / ':' / '=')。"
|
||||||
|
f"源码片段 {snippet!r}。"
|
||||||
|
f"期望形态:`T*`(1 层)/ `T**`(2 层),T 可为裸名 / t.X / mod.Type / "
|
||||||
|
f"rawptr[T] / t.CPtr[T]。"
|
||||||
|
f"正确示例:`a: t.CPtr*` / `a: rawptr*` / `a: t.X**` / "
|
||||||
|
f"`a: rawptr[ast.AST*]` / `def f(p: int*) -> int**`。"
|
||||||
|
)
|
||||||
|
return expr
|
||||||
|
|
||||||
|
# 类型结束后缀:'_AtPtrSuffix' 的判据表
|
||||||
|
# - 全局(任意表达式上下文):'*'/'**' 串后紧跟这些 operator 时,乘法/幂本就缺右操作数
|
||||||
|
# (即原语法错误),故按指针后缀处理不会改变任何合法表达式的语义;
|
||||||
|
# - 注解模式额外允许语句结束位置(NEWLINE/NL/ENDMARKER/';')。
|
||||||
|
_PTR_SUFFIX_END_OPS: tuple[str, ...] = (']', ',', ')', ':', '=')
|
||||||
|
_PTR_SUFFIX_STMT_END_OPS: tuple[str, ...] = (';',)
|
||||||
|
# 指针后缀「强转调用」后缀:'*'/'**' 串后**紧贴**(无空白)'(' 时,写成 C 风格
|
||||||
|
# 强转调用 `T*(x)`。'(' **不**放进 `_PTR_SUFFIX_END_OPS`:`a * (b)` 是合法乘法
|
||||||
|
# ('(' 正是乘法的右操作数起点),无条件把 '(' 计入会误伤乘法;故另立一条
|
||||||
|
# 「紧贴」判据(见 `_AtPtrSuffixCall`),只认 `T*(` 这种**无空白**的强转写法。
|
||||||
|
_PTR_SUFFIX_CALL_OP: str = '('
|
||||||
|
|
||||||
|
def _AtPtrSuffix(self, base: N.expr) -> bool:
|
||||||
|
"""判定当前 '*'/'**' 串是否为 C 风格指针后缀(作用于 base)。
|
||||||
|
|
||||||
|
判据(两条同时成立):
|
||||||
|
1. base 是"类型原子"形态(Name / Attribute / Subscript)—— 即 ``ast.AST``、
|
||||||
|
``t.CArray[X, N]``、``rawptr[X]`` 之类;
|
||||||
|
2. 该 '*'/'**' 串(可跨括号内换行 NL)之后紧跟"无右操作数"位置:
|
||||||
|
``]`` / ``,`` / ``)`` / ``:`` / ``=``(全局),或注解模式下的
|
||||||
|
NEWLINE / NL / ENDMARKER / ``;``。
|
||||||
|
|
||||||
|
因此合法表达式 ``x[a * b]`` / ``f(x * y)`` / ``x[1 * 2]`` / ``a ** b``
|
||||||
|
('*' 之后是操作数)永不被判为指针后缀。
|
||||||
|
"""
|
||||||
|
if not isinstance(base, (N.Name, N.Attribute, N.Subscript)):
|
||||||
|
return False
|
||||||
|
j: int = 0
|
||||||
|
stars: int = 0
|
||||||
|
while True:
|
||||||
|
tok: Token = self._Peek(j)
|
||||||
|
if tok.Type == OP and tok.Value in ('*', '**'):
|
||||||
|
stars += 1 if tok.Value == '*' else 2
|
||||||
|
j += 1
|
||||||
|
elif tok.Type == NL:
|
||||||
|
j += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if stars == 0:
|
||||||
|
return False
|
||||||
|
nxt: Token = self._Peek(j)
|
||||||
|
if nxt.Type == OP and nxt.Value in self._PTR_SUFFIX_END_OPS:
|
||||||
|
return True
|
||||||
|
if self._InTypeAnnotation:
|
||||||
|
if nxt.Type in (NEWLINE, NL, ENDMARKER):
|
||||||
|
return True
|
||||||
|
if nxt.Type == OP and nxt.Value in self._PTR_SUFFIX_STMT_END_OPS:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _ConsumePtrSuffix(self, expr: N.expr) -> N.expr:
|
||||||
|
"""消费连续的 C 风格指针后缀('*' 一层 / '**' 两层),展开为 base | t.CPtr 链。"""
|
||||||
while True:
|
while True:
|
||||||
tok: Token = self._Peek()
|
tok: Token = self._Peek()
|
||||||
if tok.Type == OP and tok.Value == '*':
|
if tok.Type == OP and tok.Value == '*':
|
||||||
self._Next()
|
self._Next()
|
||||||
|
self._PtrSuffixTail = tok
|
||||||
expr = self._MakePtrType(expr, tok)
|
expr = self._MakePtrType(expr, tok)
|
||||||
elif tok.Type == OP and tok.Value == '**':
|
elif tok.Type == OP and tok.Value == '**':
|
||||||
self._Next()
|
self._Next()
|
||||||
|
self._PtrSuffixTail = tok
|
||||||
expr = self._MakePtrType(expr, tok)
|
expr = self._MakePtrType(expr, tok)
|
||||||
expr = self._MakePtrType(expr, tok)
|
expr = self._MakePtrType(expr, tok)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
|
def _AtCastOperand(self) -> bool:
|
||||||
|
"""Whether the current token may start the operand of a paren-less cast `(T*)x`.
|
||||||
|
|
||||||
|
'(' is deliberately absent: `(T*)(x)` is already handled by the call trailer,
|
||||||
|
so that path's AST stays untouched. NL is not skipped either: a line break
|
||||||
|
after ')' starts a new statement, not a cast operand.
|
||||||
|
"""
|
||||||
|
tok: Token = self._Peek()
|
||||||
|
if tok.Type in (NAME, NUMBER, STRING):
|
||||||
|
return True
|
||||||
|
return tok.Type == OP and tok.Value in ('[', '{', '*', '&', '-', '+', '~')
|
||||||
|
|
||||||
|
def _AtOperandAfterStarRun(self) -> bool:
|
||||||
|
"""当前 '*'/'**' 串之后是否还有操作数起点(可跨括号内换行 NL)。
|
||||||
|
|
||||||
|
与 `_AtPtrSuffix` 互补:后者判「'*' 后是无右操作数位置」(']' / ',' / ')' /
|
||||||
|
':' / '=' / 行尾 / ';' ⇒ 指针后缀),本方法判其反面('*' 后是 NAME / NUMBER /
|
||||||
|
STRING / FSTRING 或 '(' / '[' / '{' / 一元前缀 ⇒ 乘法)。用于下标实参内区分
|
||||||
|
`t.CArray[T, A * B]`(乘法)与 `t.CArray[T*, 4]`(指针后缀)。
|
||||||
|
"""
|
||||||
|
j: int = 0
|
||||||
|
while True:
|
||||||
|
tok: Token = self._Peek(j)
|
||||||
|
if tok.Type == OP and tok.Value in ('*', '**'):
|
||||||
|
j += 1
|
||||||
|
elif tok.Type == NL:
|
||||||
|
j += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
nxt: Token = self._Peek(j)
|
||||||
|
if nxt.Type in (NAME, NUMBER, STRING, FSTRING):
|
||||||
|
return True
|
||||||
|
return nxt.Type == OP and nxt.Value in ('(', '[', '{', '*', '&', '-', '+', '~', '...')
|
||||||
|
|
||||||
|
def _AtPtrSuffixCall(self, base: N.expr) -> bool:
|
||||||
|
"""判定当前 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用**。
|
||||||
|
|
||||||
|
判据(三条同时成立):
|
||||||
|
1. base 是"类型原子"形态(Name / Attribute / Subscript)—— 与
|
||||||
|
`_AtPtrSuffix` 的判据 1 同源(`ast.Call` / `list[ast.AST*]` 之类);
|
||||||
|
2. base 与 '*'/'**' 串**紧贴**(源码字符相邻,无空白/换行);
|
||||||
|
3. 该 '*'/'**' 串(串内**逐个紧贴**)之后**紧贴**一个 '('。
|
||||||
|
|
||||||
|
即整段写作 **无空白**的 `T*(x)`(C 风格强转写法)。为什么 '(' 不在
|
||||||
|
`_PTR_SUFFIX_END_OPS` 里:`a * (b)` 是合法乘法('(' 可作乘法右操作数的
|
||||||
|
起点),把 '(' 无条件计入表会误伤乘法。故此处叠加"前驱是类型原子 + 两侧
|
||||||
|
紧贴"两条判据:`a * (b + c)` / `f(x) * (y)` / `n * (m - 1)` /
|
||||||
|
`(i + 1) * (j)`('*' 前后有空白,且前驱多为非类型原子)仍按乘法解析。
|
||||||
|
"""
|
||||||
|
if not isinstance(base, (N.Name, N.Attribute, N.Subscript)):
|
||||||
|
return False
|
||||||
|
first: Token = self._Peek(0)
|
||||||
|
prev: Token = self._Prev()
|
||||||
|
if prev.Type in (NEWLINE, NL) or prev.End != first.Start:
|
||||||
|
return False # base 与 '*'/'**' 之间有空白/换行:非 C 风格强转写法
|
||||||
|
j: int = 0
|
||||||
|
stars: int = 0
|
||||||
|
last_end: int = -1
|
||||||
|
while True:
|
||||||
|
tok: Token = self._Peek(j)
|
||||||
|
if tok.Type == OP and tok.Value in ('*', '**'):
|
||||||
|
if last_end != -1 and tok.Start != last_end:
|
||||||
|
return False # 星串内部出现空白:非紧邻,不判为强转
|
||||||
|
last_end = tok.End
|
||||||
|
stars += 1
|
||||||
|
j += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if stars == 0:
|
||||||
|
return False
|
||||||
|
nxt: Token = self._Peek(j)
|
||||||
|
return nxt.Type == OP and nxt.Value == self._PTR_SUFFIX_CALL_OP \
|
||||||
|
and nxt.Start == last_end
|
||||||
|
|
||||||
|
def _ConsumePtrSuffixCall(self, base: N.expr, start: Token) -> N.expr:
|
||||||
|
"""把已认定为 `T*(...)` 的指针后缀强转解析为**调用**。
|
||||||
|
|
||||||
|
产物与既有强转形态 `(T | t.CPtr)(...)` 逐字同形:同为 `ast.Call`
|
||||||
|
(func = `T` 展开的 `base | t.CPtr` 链,args/keywords = 括号实参),
|
||||||
|
不新造第二套 cast 表示。
|
||||||
|
"""
|
||||||
|
func: N.expr = self._ConsumePtrSuffix(base)
|
||||||
|
return self._ParseCallTail(func, start)
|
||||||
|
|
||||||
def _MakePtrType(self, base: N.expr, tok: Token) -> N.expr:
|
def _MakePtrType(self, base: N.expr, tok: Token) -> N.expr:
|
||||||
"""构造一层指针包装:base | t.CPtr。"""
|
"""构造一层指针包装:base | t.CPtr。"""
|
||||||
cptr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
cptr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||||||
|
|||||||
+156
-6
@@ -22,9 +22,56 @@ from lib.vast.Tokenizer import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# virtual / novirtual 修饰符(软关键字)
|
||||||
|
# ======================================================================
|
||||||
|
# 语法:`virtual class` / `novirtual class` / `virtual def` / `novirtual def`
|
||||||
|
# (修饰符写在 class/def **之前**,允许与修饰对象同行,也允许各占一行)。
|
||||||
|
#
|
||||||
|
# 语义:与既有装饰器**同一套表示**——virtual → `@t.CVTable`,
|
||||||
|
# novirtual → `@t.NoVTable`。本层只"合成装饰器节点",不新增第二套表示:
|
||||||
|
# - 类级:CVTable 强制有 vtable / NoVTable 强制无 vtable
|
||||||
|
# (TPC:HandlesClassDef._resolve_class_flags 读 ClassDef.decorator_list
|
||||||
|
# 的 CVTable/NoVTable;TPV:ClassGenBase._detect_vtable_status)
|
||||||
|
# - 方法级:CVTable 进虚表槽 / NoVTable 不进虚表槽
|
||||||
|
# (TPV:ClassGenBase._should_method_be_virtual;TPC:同判据见
|
||||||
|
# SymbolUtils.ClassVtableSlotMethods / 方法收集处)
|
||||||
|
#
|
||||||
|
# 行号守恒:合成节点的位置取"修饰符 token"的位置,ClassDef/FunctionDef 自身的
|
||||||
|
# lineno/col_offset 仍是源码里 class/def 的真实位置 ⇒ 与手写装饰器形态在
|
||||||
|
# 行号上也不产生任何偏移(vs 文本插入装饰器行会整篇 +1 行)。
|
||||||
|
#
|
||||||
|
# 软关键字边界(非保留字):
|
||||||
|
# - 只在**语句起始**且后随 class/def 时生效;作标识符的其它位置
|
||||||
|
# (`x.virtual` / `f(virtual)` / `d = {"virtual": 1}`)逐字不变;
|
||||||
|
# - 语句起始但后随非 class/def ⇒ **响亮失败**(绝不静默丢弃修饰符);
|
||||||
|
# - `virtual def` / `novirtual def` 出现在类体之外(模块级 / 函数体 / 分支体)
|
||||||
|
# ⇒ **响亮失败**。
|
||||||
|
_VIRTUAL_MARKERS: dict[str, str] = {'virtual': 'CVTable', 'novirtual': 'NoVTable'}
|
||||||
|
# def 的别名(与 Tokenizer.SOFT_KEYWORDS 的 def 系列同集合,**不含** extern——
|
||||||
|
# extern 由 _ParseExternDef 单独解析,不能走 _ParseFunctionDef)
|
||||||
|
_DEF_ALIASES: tuple[str, ...] = ('fn', 'fun', 'func', 'function')
|
||||||
|
_VIRTUAL_POSITION_HINT: str = (
|
||||||
|
'可写位置:类级 `virtual class C:` / `novirtual class C:`;'
|
||||||
|
'方法级 `virtual def m(self): ...` / `novirtual def m(self): ...`'
|
||||||
|
'(**只能写在类体内**:virtual class / class / novirtual class 的类体)'
|
||||||
|
)
|
||||||
|
_VIRTUAL_EXAMPLE: str = (
|
||||||
|
'virtual class Shape: / novirtual class Node: / '
|
||||||
|
'class A: virtual def run(self) -> int: ... / '
|
||||||
|
'novirtual class B: novirtual def helper(self) -> int: ...'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Parser(ExprParserMixin):
|
class Parser(ExprParserMixin):
|
||||||
"""Viper/Python 完整语法分析器。"""
|
"""Viper/Python 完整语法分析器。"""
|
||||||
|
|
||||||
|
# virtual / novirtual 修饰符:语句起始 + 紧跟 class/def 时才生效(软关键字)。
|
||||||
|
# _InClassBody:当前正在解析的套件的**直接拥有者**是否为 ClassDef
|
||||||
|
# (由 _ParseSuite 按 owner_is_class 维护),用于 fail-fast
|
||||||
|
# "virtual def / novirtual def 只能直接写在类体内"。
|
||||||
|
_InClassBody: bool = False
|
||||||
|
|
||||||
# ==================================================================
|
# ==================================================================
|
||||||
# 模块级入口
|
# 模块级入口
|
||||||
# ==================================================================
|
# ==================================================================
|
||||||
@@ -67,6 +114,14 @@ class Parser(ExprParserMixin):
|
|||||||
# def 分派之前
|
# def 分派之前
|
||||||
if v == 'define' and self._Peek(1).Type == NAME:
|
if v == 'define' and self._Peek(1).Type == NAME:
|
||||||
return self._ParseDefineDef()
|
return self._ParseDefineDef()
|
||||||
|
# virtual / novirtual 修饰符(软关键字):只在"语句起始 + 紧跟 class/def"
|
||||||
|
# 时生效,合成既有装饰器 @t.CVTable / @t.NoVTable(见 _ParseVirtualModified)。
|
||||||
|
# 放在 def/class 分派之前:修饰符必须先被消费,class/def 才能照常分派。
|
||||||
|
# 与软关键字 fn→def 的先后关系:`.vp` 源码在进入本解析器之前已过
|
||||||
|
# SymbolUtils.ExpandSoftKeywords(fn/fun/func/function → def),
|
||||||
|
# 故 `virtual fn m()` 在此已是 `virtual def m()`;本层只认 class/def。
|
||||||
|
if v in _VIRTUAL_MARKERS:
|
||||||
|
return self._ParseVirtualModified()
|
||||||
# 软关键字函数定义:fn/fun/func/function + 标识符
|
# 软关键字函数定义:fn/fun/func/function + 标识符
|
||||||
if v in SOFT_KEYWORDS and self._Peek(1).Type == NAME:
|
if v in SOFT_KEYWORDS and self._Peek(1).Type == NAME:
|
||||||
return self._ParseFunctionDef()
|
return self._ParseFunctionDef()
|
||||||
@@ -520,8 +575,83 @@ class Parser(ExprParserMixin):
|
|||||||
return self._ParseFunctionDef(is_async=True, decorators=decorator_list)
|
return self._ParseFunctionDef(is_async=True, decorators=decorator_list)
|
||||||
if tok.Type == NAME and tok.Value == 'class':
|
if tok.Type == NAME and tok.Value == 'class':
|
||||||
return self._ParseClassDef(decorators=decorator_list)
|
return self._ParseClassDef(decorators=decorator_list)
|
||||||
|
if tok.Type == NAME and tok.Value in _VIRTUAL_MARKERS:
|
||||||
|
# `@deco` 与 `virtual class`/`virtual def` 组合:合成标记追加在既有装饰器之后
|
||||||
|
return self._ParseVirtualModified(decorators=decorator_list)
|
||||||
raise SyntaxError(f"decorator must be followed by def/class, got {tok.Value!r}")
|
raise SyntaxError(f"decorator must be followed by def/class, got {tok.Value!r}")
|
||||||
|
|
||||||
|
# ==================================================================
|
||||||
|
# virtual / novirtual 修饰符 + class/def
|
||||||
|
# ==================================================================
|
||||||
|
def _SourceLine(self, lineno: int) -> str:
|
||||||
|
"""取 lineno 行的源码文本(去掉行终止符),供 fail-fast 报文附源码片段。"""
|
||||||
|
starts: list[int] = self.LineStarts
|
||||||
|
if lineno < 1 or lineno > len(starts):
|
||||||
|
return ''
|
||||||
|
begin: int = starts[lineno - 1]
|
||||||
|
end: int = starts[lineno] if lineno < len(starts) else len(self.Source)
|
||||||
|
return self.Source[begin:end].rstrip('\n').rstrip('\r')
|
||||||
|
|
||||||
|
def _VirtualSyntaxError(self, detail: str, tok: Token, extra: str = '') -> SyntaxError:
|
||||||
|
"""构造 virtual/novirtual 修饰符非法形态的 fail-fast 报文。
|
||||||
|
|
||||||
|
报文含 行:列 + 源码片段 + 该修饰符允许出现的位置 + 正确示例;行尾另附
|
||||||
|
`at line N`(TPC 既有诊断路径按该正则兜底提取行号,见
|
||||||
|
HandlesImports._ParseFailureSite),文件名由调用方补全。
|
||||||
|
"""
|
||||||
|
return SyntaxError(
|
||||||
|
f"[TPC] virtual/novirtual 修饰符非法({detail}):行 {tok.Line} 列 {tok.Col},"
|
||||||
|
f"源码片段 {self._SourceLine(tok.Line).strip()!r}。"
|
||||||
|
f"{_VIRTUAL_POSITION_HINT}。正确示例:{_VIRTUAL_EXAMPLE}。"
|
||||||
|
+ (f" {extra}" if extra else '')
|
||||||
|
+ " (修饰符不会被静默忽略:非法形态一律终止编译)"
|
||||||
|
+ f" at line {tok.Line}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _ParseVirtualModified(self, decorators: list[N.expr] | None = None) -> N.stmt:
|
||||||
|
"""`virtual class` / `novirtual class` / `virtual def` / `novirtual def`。
|
||||||
|
|
||||||
|
合成 `@t.CVTable` / `@t.NoVTable` 装饰器后交给 _ParseClassDef /
|
||||||
|
_ParseFunctionDef,产出与**手写同名装饰器**逐字段同形的 AST
|
||||||
|
(decorator_list 多一个 Attribute(Name('t', Load), 'CVTable'|'NoVTable', Load)),
|
||||||
|
故下游(_resolve_class_flags / _should_method_be_virtual 同族判据)零新增分支。
|
||||||
|
|
||||||
|
非法形态**响亮失败**(不静默丢修饰符):
|
||||||
|
- 后随非 class/def;
|
||||||
|
- `virtual def` / `novirtual def` 不在类体内(模块级 / 函数体 / 分支体)。
|
||||||
|
"""
|
||||||
|
mod: Token = self._Peek() # virtual / novirtual
|
||||||
|
nxt: Token = self._Peek(1)
|
||||||
|
marker: str = _VIRTUAL_MARKERS[mod.Value]
|
||||||
|
# 后随必须是 class / def;def 的别名 fn/fun/func/function 同样接受
|
||||||
|
# (与 TPV 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的
|
||||||
|
# fn→def 展开只命中**行首**的 fn,`virtual fn m()` 不会被先展开,
|
||||||
|
# 故此处必须显式接受别名(否则两端对同一源码一端报错一端通过)。
|
||||||
|
if nxt.Type != NAME or (nxt.Value != 'class'
|
||||||
|
and nxt.Value != 'def'
|
||||||
|
and nxt.Value not in _DEF_ALIASES):
|
||||||
|
nxt_text: str = ('(行尾)' if nxt.Type in (NEWLINE, NL, ENDMARKER)
|
||||||
|
else repr(nxt.Value))
|
||||||
|
raise self._VirtualSyntaxError(
|
||||||
|
f"{mod.Value!r} 后随 {nxt_text}(非 class/def)", mod,
|
||||||
|
f"`{mod.Value}` 只在 class/def(def 的别名 fn/fun/func/function)"
|
||||||
|
f"之前生效;若此处本意是普通标识符名,"
|
||||||
|
f"请改名(该词在语句起始会被判为修饰符)。")
|
||||||
|
if nxt.Value == 'def' and not self._InClassBody:
|
||||||
|
raise self._VirtualSyntaxError(
|
||||||
|
f'{mod.Value} def 出现在类体之外', mod,
|
||||||
|
f'{mod.Value} def 描述的是「类体内方法的虚/非虚」,'
|
||||||
|
f'模块级 / 函数体 / 分支体内没有可归属的类。')
|
||||||
|
self._Next() # 消费修饰符
|
||||||
|
deco: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||||||
|
attr=marker, ctx=N.Load())
|
||||||
|
self._FixLoc(deco, mod)
|
||||||
|
deco_list: list[N.expr] = list(decorators or [])
|
||||||
|
deco_list.append(deco)
|
||||||
|
if nxt.Value == 'class':
|
||||||
|
return self._ParseClassDef(decorators=deco_list)
|
||||||
|
return self._ParseFunctionDef(decorators=deco_list)
|
||||||
|
|
||||||
# ==================================================================
|
# ==================================================================
|
||||||
# 函数定义(含软关键字 fn/fun/func/function)
|
# 函数定义(含软关键字 fn/fun/func/function)
|
||||||
# ==================================================================
|
# ==================================================================
|
||||||
@@ -766,7 +896,7 @@ class Parser(ExprParserMixin):
|
|||||||
break
|
break
|
||||||
self._ExpectOp(')')
|
self._ExpectOp(')')
|
||||||
self._ExpectOp(':')
|
self._ExpectOp(':')
|
||||||
body: list[N.stmt] = self._ParseSuite()
|
body: list[N.stmt] = self._ParseSuite(owner_is_class=True)
|
||||||
node: N.ClassDef = N.ClassDef(
|
node: N.ClassDef = N.ClassDef(
|
||||||
name=name_tok.Value, bases=bases, keywords=keywords, body=body,
|
name=name_tok.Value, bases=bases, keywords=keywords, body=body,
|
||||||
decorator_list=decorators or [], type_params=type_params,
|
decorator_list=decorators or [], type_params=type_params,
|
||||||
@@ -777,11 +907,18 @@ class Parser(ExprParserMixin):
|
|||||||
# ==================================================================
|
# ==================================================================
|
||||||
# 语句块(套件)
|
# 语句块(套件)
|
||||||
# ==================================================================
|
# ==================================================================
|
||||||
def _ParseSuite(self) -> list[N.stmt]:
|
def _ParseSuite(self, owner_is_class: bool = False) -> list[N.stmt]:
|
||||||
"""解析语句块:':' NEWLINE INDENT ... DEDENT 或单行简单语句。
|
"""解析语句块:':' NEWLINE INDENT ... DEDENT 或单行简单语句。
|
||||||
|
|
||||||
块首允许出现空行/注释行(NL),跳过后再检测 INDENT。
|
块首允许出现空行/注释行(NL),跳过后再检测 INDENT。
|
||||||
|
|
||||||
|
owner_is_class:本套件的**直接拥有者**是否 ClassDef。套件解析期间置
|
||||||
|
`_InClassBody`,供 `_ParseVirtualModified` 判 `virtual def`/`novirtual def`
|
||||||
|
是否"直接写在类体内"(函数体 / 分支体 / 循环体都不算)。默认 False ⇒
|
||||||
|
除类体外的所有套件拥有者天然正确,无需逐个改动。
|
||||||
"""
|
"""
|
||||||
|
prev_in_class: bool = self._InClassBody
|
||||||
|
self._InClassBody = owner_is_class
|
||||||
# 已消费 ':' 后的情况:检查是否换行
|
# 已消费 ':' 后的情况:检查是否换行
|
||||||
if self._At(NEWLINE):
|
if self._At(NEWLINE):
|
||||||
self._Next()
|
self._Next()
|
||||||
@@ -804,8 +941,10 @@ class Parser(ExprParserMixin):
|
|||||||
body.append(extra)
|
body.append(extra)
|
||||||
self._SkipNEWLINE()
|
self._SkipNEWLINE()
|
||||||
self._Expect(DEDENT)
|
self._Expect(DEDENT)
|
||||||
|
self._InClassBody = prev_in_class
|
||||||
return body
|
return body
|
||||||
# 空套件(语法错误,宽松返回空)
|
# 空套件(语法错误,宽松返回空)
|
||||||
|
self._InClassBody = prev_in_class
|
||||||
return []
|
return []
|
||||||
# 单行套件:statement (; statement)* NEWLINE
|
# 单行套件:statement (; statement)* NEWLINE
|
||||||
body = [self.ParseStatement()]
|
body = [self.ParseStatement()]
|
||||||
@@ -815,6 +954,11 @@ class Parser(ExprParserMixin):
|
|||||||
break
|
break
|
||||||
body.append(self.ParseStatement())
|
body.append(self.ParseStatement())
|
||||||
self._ExpectNEWLINE()
|
self._ExpectNEWLINE()
|
||||||
|
# 吞掉紧跟在单行套件之后的注释行/空行 NL:多行套件分支在 DEDENT 之前
|
||||||
|
# 已由循环内 _SkipNEWLINE 吞掉这些 NL,单行套件没有 DEDENT,若不在此吞掉,
|
||||||
|
# 调用方紧随其后的 elif/else/except/finally 续接子句会被 NL 挡住 → 分支链断裂。
|
||||||
|
self._SkipNL()
|
||||||
|
self._InClassBody = prev_in_class
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def _Expect(self, tok_type: str, value: str | None = None) -> Token:
|
def _Expect(self, tok_type: str, value: str | None = None) -> Token:
|
||||||
@@ -1227,9 +1371,15 @@ def Parse(source: str) -> N.Module:
|
|||||||
|
|
||||||
|
|
||||||
def _ComputeLineStarts(source: str) -> list[int]:
|
def _ComputeLineStarts(source: str) -> list[int]:
|
||||||
"""计算源码所有行起始字符索引(含首行 0)。"""
|
"""计算源码所有行起始字符索引(含首行 0)。
|
||||||
|
|
||||||
|
Uses C-level str.find instead of a per-character Python loop (large .vp
|
||||||
|
sources: millions of characters scanned once per parse).
|
||||||
|
"""
|
||||||
starts: list[int] = [0]
|
starts: list[int] = [0]
|
||||||
for i, ch in enumerate(source):
|
Find = source.find
|
||||||
if ch == '\n':
|
i: int = Find('\n')
|
||||||
starts.append(i + 1)
|
while i >= 0:
|
||||||
|
starts.append(i + 1)
|
||||||
|
i = Find('\n', i + 1)
|
||||||
return starts
|
return starts
|
||||||
@@ -179,7 +179,7 @@ class Tokenizer:
|
|||||||
if self.Pos >= self.Length:
|
if self.Pos >= self.Length:
|
||||||
break
|
break
|
||||||
continue
|
continue
|
||||||
if ch in ' \t\f':
|
if ch in ' \t\f\v':
|
||||||
self.Pos += 1
|
self.Pos += 1
|
||||||
continue
|
continue
|
||||||
if ch == '\r':
|
if ch == '\r':
|
||||||
@@ -258,7 +258,9 @@ class Tokenizer:
|
|||||||
elif ch == '\t':
|
elif ch == '\t':
|
||||||
col = (col // 8 + 1) * 8
|
col = (col // 8 + 1) * 8
|
||||||
self.Pos += 1
|
self.Pos += 1
|
||||||
elif ch == '\f':
|
elif ch == '\f' or ch == '\v':
|
||||||
|
# \f(换页)与 \v(垂直制表)不计入缩进列,直接跳过;\v 与 Viper
|
||||||
|
# 词法层 includes/ast/lexer.vp `_is_space` 一致(对齐语言定义)。
|
||||||
self.Pos += 1
|
self.Pos += 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|||||||
+90
-4
@@ -19,6 +19,7 @@ ExpandSoftKeywords 预处理。
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os as _os
|
import os as _os
|
||||||
|
import re as _re
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
|
|
||||||
from lib.vast.Nodes import AST
|
from lib.vast.Nodes import AST
|
||||||
@@ -56,27 +57,112 @@ def _ParsePure(source: str, mode: str) -> AST:
|
|||||||
|
|
||||||
# C 加速解析器(vast_c 扩展):可用则优先,输出与 vast.parse 结构完全一致。
|
# C 加速解析器(vast_c 扩展):可用则优先,输出与 vast.parse 结构完全一致。
|
||||||
# VAST_USE_C=0 禁用 C 路径;=strict 时 C 路径异常直接抛出(fail-fast)。
|
# VAST_USE_C=0 禁用 C 路径;=strict 时 C 路径异常直接抛出(fail-fast)。
|
||||||
|
#
|
||||||
|
# 回退策略(响亮回退):C 路径抛异常时回退纯 Python,并向 stderr 打印一行可诊断
|
||||||
|
# 信息(文件名 + 异常)。该回退**不会**改变产出——纯 Python 是本包语义基准,
|
||||||
|
# 故绝不静默产出与纯 Python 不同的 AST。连续相同的(文件, 异常)只打印一次。
|
||||||
_VAST_C_PARSE = None
|
_VAST_C_PARSE = None
|
||||||
try:
|
try:
|
||||||
from lib.vast_c import parse as _VAST_C_PARSE
|
from lib.vast_c import parse as _VAST_C_PARSE
|
||||||
except Exception:
|
except Exception:
|
||||||
_VAST_C_PARSE = None
|
_VAST_C_PARSE = None
|
||||||
|
|
||||||
|
_CFallbackSeen: set = set()
|
||||||
|
|
||||||
|
|
||||||
|
def _ReportCFallback(filename: str, exc: BaseException) -> None:
|
||||||
|
"""C 解析失败回退时的可诊断信息(去重,写 stderr)。"""
|
||||||
|
detail: str = f'{type(exc).__name__}: {exc}'
|
||||||
|
key = (filename, detail)
|
||||||
|
if key in _CFallbackSeen:
|
||||||
|
return
|
||||||
|
_CFallbackSeen.add(key)
|
||||||
|
_sys.stderr.write(
|
||||||
|
f'[vast] C 解析器失败,回退纯 Python:{filename}: {detail}\n')
|
||||||
|
|
||||||
|
|
||||||
|
# vast 语法错误统一报文:解析器原生消息末段带 "at line N"(ExprParser/Parser 各站点同款),
|
||||||
|
# 取出行号后补齐源码行与列号,用户看到的是单行 [TPC] 报文而不是解析器内部调用链。
|
||||||
|
_SYNTAX_LINE_RE: _re.Pattern[str] = _re.compile(r'at line (\d+)')
|
||||||
|
# 原生消息里的 token 原文(如 NAME('foo') / OP('*=') / INDENT(' ')),用于定位列号
|
||||||
|
_SYNTAX_TOKEN_RE: _re.Pattern[str] = _re.compile(r"\((?:'([^']*)'|\"([^\"]*)\")\)")
|
||||||
|
|
||||||
|
|
||||||
|
def _SourceLine(source: str, lineno: int) -> str:
|
||||||
|
"""取 1-based 第 lineno 行源码(行号缺失/越界返回空串)。"""
|
||||||
|
if lineno <= 0:
|
||||||
|
return ''
|
||||||
|
lines: list[str] = source.splitlines()
|
||||||
|
if lineno > len(lines):
|
||||||
|
return ''
|
||||||
|
return lines[lineno - 1]
|
||||||
|
|
||||||
|
|
||||||
|
def _LocateToken(msg: str, line_text: str) -> int:
|
||||||
|
"""定位原生消息里 token 原文在该行的 1-based 列;找不到退回首个非空白列。"""
|
||||||
|
if not line_text:
|
||||||
|
return 0
|
||||||
|
pos: int = -1
|
||||||
|
matched = _SYNTAX_TOKEN_RE.search(msg)
|
||||||
|
if matched is not None:
|
||||||
|
tok_text: str = matched.group(1) if matched.group(1) is not None else matched.group(2)
|
||||||
|
if tok_text.strip():
|
||||||
|
pos = line_text.find(tok_text)
|
||||||
|
if pos < 0:
|
||||||
|
pos = len(line_text) - len(line_text.lstrip())
|
||||||
|
return pos + 1
|
||||||
|
|
||||||
|
|
||||||
|
def _HumanizeSyntaxError(exc: SyntaxError, source: str, filename: str) -> SyntaxError:
|
||||||
|
"""把解析器的 SyntaxError 收口成「行:列 + 源码片段 + 原始消息」的单行报文。
|
||||||
|
|
||||||
|
重新抛出的语法错误带 filename/lineno/offset/text 四要素,故上层即使用
|
||||||
|
traceback.format_exc 也只打印该文件对应行与 caret,不再漏出解析器内部调用链。
|
||||||
|
"""
|
||||||
|
msg: str = str(exc)
|
||||||
|
lineno: int = getattr(exc, 'lineno', 0) or 0
|
||||||
|
if lineno <= 0:
|
||||||
|
found = _SYNTAX_LINE_RE.search(msg)
|
||||||
|
if found is not None:
|
||||||
|
lineno = int(found.group(1))
|
||||||
|
line_text: str = getattr(exc, 'text', '') or _SourceLine(source, lineno)
|
||||||
|
col: int = getattr(exc, 'offset', 0) or 0
|
||||||
|
if col <= 0:
|
||||||
|
col = _LocateToken(msg, line_text)
|
||||||
|
new: SyntaxError = SyntaxError(
|
||||||
|
f"[TPC] 语法错误({msg}):行 {lineno} 列 {col},源码片段 {line_text.strip()!r}。")
|
||||||
|
new.filename = filename
|
||||||
|
new.lineno = lineno
|
||||||
|
new.offset = col
|
||||||
|
new.text = line_text
|
||||||
|
return new
|
||||||
|
|
||||||
|
|
||||||
def parse(source: str, filename: str = '<unknown>', mode: str = 'exec', *,
|
def parse(source: str, filename: str = '<unknown>', mode: str = 'exec', *,
|
||||||
type_comments: bool = False, feature_version: tuple[int, int] | int | None = None) -> AST:
|
type_comments: bool = False, feature_version: tuple[int, int] | int | None = None) -> AST:
|
||||||
"""解析源码字符串为 AST(与 ast.parse 同签名、同结构)。
|
"""解析源码字符串为 AST(与 ast.parse 同签名、同结构)。
|
||||||
|
|
||||||
优先使用 C 加速解析器 vast_c(输出结构完全一致),异常时回退纯 Python vast;
|
优先使用 C 加速解析器 vast_c(输出结构完全一致),异常时回退纯 Python vast
|
||||||
环境变量 VAST_USE_C:'0' 禁用 C 路径,'strict' 时 C 路径异常直接抛出。
|
并打印可诊断信息;环境变量 VAST_USE_C:'0' 禁用 C 路径,'strict' 时 C 路径
|
||||||
|
异常直接抛出。两条路径的 SyntaxError 都收口成带 文件/行/列/源码片段 的统一
|
||||||
|
报文(不再向用户漏出解析器内部 traceback)。
|
||||||
"""
|
"""
|
||||||
if _VAST_C_PARSE is not None and _os.environ.get('VAST_USE_C', '1') != '0':
|
if _VAST_C_PARSE is not None and _os.environ.get('VAST_USE_C', '1') != '0':
|
||||||
try:
|
try:
|
||||||
return _VAST_C_PARSE(source, mode)
|
return _VAST_C_PARSE(source, mode)
|
||||||
except Exception:
|
except SyntaxError as _exc:
|
||||||
|
if _os.environ.get('VAST_USE_C') == 'strict':
|
||||||
|
raise _HumanizeSyntaxError(_exc, source, filename) from None
|
||||||
|
_ReportCFallback(filename, _exc)
|
||||||
|
except Exception as _exc:
|
||||||
if _os.environ.get('VAST_USE_C') == 'strict':
|
if _os.environ.get('VAST_USE_C') == 'strict':
|
||||||
raise
|
raise
|
||||||
return _ParsePure(source, mode)
|
_ReportCFallback(filename, _exc)
|
||||||
|
try:
|
||||||
|
return _ParsePure(source, mode)
|
||||||
|
except SyntaxError as _exc:
|
||||||
|
raise _HumanizeSyntaxError(_exc, source, filename) from None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
__all__: list[str] = [
|
__all__: list[str] = [
|
||||||
|
|||||||
Binary file not shown.
+412
-19
@@ -46,6 +46,22 @@ typedef struct {
|
|||||||
int EndOff;
|
int EndOff;
|
||||||
} Token;
|
} Token;
|
||||||
|
|
||||||
|
/* Token 类型名(与 Python vast.Tokenizer 的常量同名,使两侧报文措辞一致)。 */
|
||||||
|
static const char *TokKindName(TokenKind K) {
|
||||||
|
switch (K) {
|
||||||
|
case TK_NAME: return "NAME";
|
||||||
|
case TK_NUMBER: return "NUMBER";
|
||||||
|
case TK_STRING: return "STRING";
|
||||||
|
case TK_FSTRING: return "FSTRING";
|
||||||
|
case TK_OP: return "OP";
|
||||||
|
case TK_NEWLINE: return "NEWLINE";
|
||||||
|
case TK_NL: return "NL";
|
||||||
|
case TK_INDENT: return "INDENT";
|
||||||
|
case TK_DEDENT: return "DEDENT";
|
||||||
|
default: return "ENDMARKER";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ================= 词法分析器(Lexer) ================= */
|
/* ================= 词法分析器(Lexer) ================= */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -81,7 +97,11 @@ typedef struct {
|
|||||||
int Error; /* 语法错误标志(可回退) */
|
int Error; /* 语法错误标志(可回退) */
|
||||||
const char *ErrMsg;
|
const char *ErrMsg;
|
||||||
int ErrLine;
|
int ErrLine;
|
||||||
|
Token *ErrTok; /* 出错处的 token(供报错带上类型与原文) */
|
||||||
int FStringExprEnd; /* f-string 表达式段结束位置(body 内偏移) */
|
int FStringExprEnd; /* f-string 表达式段结束位置(body 内偏移) */
|
||||||
|
int InTypeAnnotation; /* 类型注解模式:'*'/'**' 允许按 C 风格指针后缀判定 */
|
||||||
|
int InClassBody; /* 当前套件的直接拥有者是否为 ClassDef(virtual def 判据) */
|
||||||
|
Token *PtrSuffixTail; /* 最近消费的 C 指针后缀尾 token(识别 (T*)x 无括号强转) */
|
||||||
} Parser;
|
} Parser;
|
||||||
|
|
||||||
/* vast.nodes 模块与节点类缓存(进程级惰性初始化) */
|
/* vast.nodes 模块与节点类缓存(进程级惰性初始化) */
|
||||||
@@ -481,7 +501,9 @@ static void HandleIndent(Lexer *L) {
|
|||||||
} else if (Ch == '\t') {
|
} else if (Ch == '\t') {
|
||||||
Col = (Col / 8 + 1) * 8;
|
Col = (Col / 8 + 1) * 8;
|
||||||
L->Pos++;
|
L->Pos++;
|
||||||
} else if (Ch == '\f') {
|
} else if (Ch == '\f' || Ch == '\v') {
|
||||||
|
/* \f(换页)与 \v(垂直制表)不计入缩进列;\v 对齐 Viper 词法层
|
||||||
|
* includes/ast/lexer.vp 的 _is_space(与 Python Tokenizer 同口径)。 */
|
||||||
L->Pos++;
|
L->Pos++;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@@ -770,7 +792,7 @@ static void TokenizeMainLoop(Lexer *L) {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (Ch == ' ' || Ch == '\t' || Ch == '\f') {
|
if (Ch == ' ' || Ch == '\t' || Ch == '\f' || Ch == '\v') {
|
||||||
L->Pos++;
|
L->Pos++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -927,6 +949,7 @@ static void SyntaxErrorAt(Parser *P, Token *T, const char *Msg) {
|
|||||||
P->Error = 1;
|
P->Error = 1;
|
||||||
P->ErrMsg = Msg;
|
P->ErrMsg = Msg;
|
||||||
P->ErrLine = T ? T->Line : 0;
|
P->ErrLine = T ? T->Line : 0;
|
||||||
|
P->ErrTok = T;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Token *ExpectOp(Parser *P, const char *S) {
|
static Token *ExpectOp(Parser *P, const char *S) {
|
||||||
@@ -1466,6 +1489,7 @@ static PyObject *JoinConstParts(PyObject *Parts) {
|
|||||||
|
|
||||||
/* ================= 表达式解析 ================= */
|
/* ================= 表达式解析 ================= */
|
||||||
|
|
||||||
|
static PyObject *MakeTAttrNode(Parser *P, const char *AttrName);
|
||||||
static PyObject *ParseExpr(Parser *P);
|
static PyObject *ParseExpr(Parser *P);
|
||||||
static PyObject *ParseConditional(Parser *P);
|
static PyObject *ParseConditional(Parser *P);
|
||||||
static PyObject *ParseDisjunction(Parser *P);
|
static PyObject *ParseDisjunction(Parser *P);
|
||||||
@@ -1507,7 +1531,7 @@ static PyObject *ParseSubExpression(Parser *P, const char *Text, Py_ssize_t Text
|
|||||||
int AbsBase);
|
int AbsBase);
|
||||||
static int SkipInnerLiteral(const char *Raw, int BodyStart, int Start, int BodyEnd);
|
static int SkipInnerLiteral(const char *Raw, int BodyStart, int Start, int BodyEnd);
|
||||||
|
|
||||||
/* f-string 内部字面量常量(仅 lineno/col_offset,对齐 vast) */
|
/* f-string 相邻字面量合并常量(仅 lineno/col_offset,对齐 vast) */
|
||||||
static PyObject *MakeFStrConstant(Parser *P, PyObject *Value, Token *OuterTok) {
|
static PyObject *MakeFStrConstant(Parser *P, PyObject *Value, Token *OuterTok) {
|
||||||
PyObject *N = MakeConstant(P, Value);
|
PyObject *N = MakeConstant(P, Value);
|
||||||
if (N != NULL) {
|
if (N != NULL) {
|
||||||
@@ -1516,6 +1540,26 @@ static PyObject *MakeFStrConstant(Parser *P, PyObject *Value, Token *OuterTok) {
|
|||||||
return N;
|
return N;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 合并常量:位置取被合并的前一个 Constant 节点(对齐 Python 的 parts[-1].lineno/col_offset)。 */
|
||||||
|
static PyObject *MakeFStrConstantLike(Parser *P, PyObject *Value, PyObject *SrcNode) {
|
||||||
|
PyObject *N = MakeConstant(P, Value);
|
||||||
|
if (N == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *Line = PyObject_GetAttrString(SrcNode, "lineno");
|
||||||
|
PyObject *Col = PyObject_GetAttrString(SrcNode, "col_offset");
|
||||||
|
if (Line != NULL) {
|
||||||
|
PyObject_SetAttrString(N, "lineno", Line);
|
||||||
|
}
|
||||||
|
if (Col != NULL) {
|
||||||
|
PyObject_SetAttrString(N, "col_offset", Col);
|
||||||
|
}
|
||||||
|
Py_XDECREF(Line);
|
||||||
|
Py_XDECREF(Col);
|
||||||
|
PyErr_Clear();
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *ParseStringConcat(Parser *P) {
|
static PyObject *ParseStringConcat(Parser *P) {
|
||||||
Token *Start = PeekTok(P, 0);
|
Token *Start = PeekTok(P, 0);
|
||||||
PyObject *Parts = NewList(); /* JoinedStr 的 values */
|
PyObject *Parts = NewList(); /* JoinedStr 的 values */
|
||||||
@@ -1607,7 +1651,7 @@ static PyObject *ParseStringConcat(Parser *P) {
|
|||||||
Py_DECREF(ConstParts);
|
Py_DECREF(ConstParts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyObject *MergedConst = MakeFStrConstant(P, Merged, Start);
|
PyObject *MergedConst = MakeFStrConstantLike(P, Merged, Last);
|
||||||
Py_DECREF(Merged);
|
Py_DECREF(Merged);
|
||||||
if (MergedConst == NULL) {
|
if (MergedConst == NULL) {
|
||||||
Py_DECREF(JVals);
|
Py_DECREF(JVals);
|
||||||
@@ -1675,7 +1719,7 @@ static PyObject *ParseStringConcat(Parser *P) {
|
|||||||
Py_DECREF(ConstParts);
|
Py_DECREF(ConstParts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyObject *MergedConst = MakeFStrConstant(P, Merged, Start);
|
PyObject *MergedConst = MakeFStrConstantLike(P, Merged, Last);
|
||||||
Py_DECREF(Merged);
|
Py_DECREF(Merged);
|
||||||
if (MergedConst == NULL) {
|
if (MergedConst == NULL) {
|
||||||
Py_DECREF(Parts);
|
Py_DECREF(Parts);
|
||||||
@@ -2510,6 +2554,170 @@ static PyObject *ParseArith(Parser *P) {
|
|||||||
return ParseBinLoop(P, ParseTerm, "+", "-", "Add", "Sub");
|
return ParseBinLoop(P, ParseTerm, "+", "-", "Add", "Sub");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============ C 风格指针后缀(* / **,对齐 vast ExprParser) ============ */
|
||||||
|
|
||||||
|
/* 构造一层指针包装 base | t.CPtr(对齐 Python _MakePtrType)。 */
|
||||||
|
static PyObject *MakePtrType(Parser *P, PyObject *Base, Token *Tok) {
|
||||||
|
PyObject *CPtr = MakeTAttrNode(P, "CPtr");
|
||||||
|
if (CPtr == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *Op = MakeSingleton(P, "BitOr");
|
||||||
|
if (Op == NULL) {
|
||||||
|
Py_DECREF(CPtr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *Argv[3] = { Base, Op, CPtr };
|
||||||
|
PyObject *Node = NewNode(P, "BinOp", Argv, 3);
|
||||||
|
Py_DECREF(Op);
|
||||||
|
Py_DECREF(CPtr);
|
||||||
|
if (Node != NULL) {
|
||||||
|
FixLoc(P, Node, Tok);
|
||||||
|
}
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 判定当前位置的 '*'/'**' 串是否为 C 风格指针后缀(作用于"类型原子"节点)。
|
||||||
|
*
|
||||||
|
* 判据(两条同时成立,对齐 Python ExprParser._AtPtrSuffix):
|
||||||
|
* 1. 节点是 Name / Attribute / Subscript(ast.AST / t.CArray[X, N] / rawptr[X] …);
|
||||||
|
* 2. '*'/'**' 串(可跨括号内换行 NL)之后紧跟"无右操作数"位置:
|
||||||
|
* ']'/','/')'/':'/'='(任意上下文)—— 乘法/幂在这些位置本就缺右操作数
|
||||||
|
* (即原语法错误),故不会改变任何合法表达式(x[a * b] / f(x * y) /
|
||||||
|
* x[1 * 2] / a ** b)的语义;注解模式额外允许 NEWLINE/NL/ENDMARKER/';'。
|
||||||
|
*/
|
||||||
|
static int AtPtrSuffixEnd(Parser *P, PyObject *Base) {
|
||||||
|
static const char *EndOps[] = {"]", ",", ")", ":", "="};
|
||||||
|
if (!IsNameOrAttrOrSub(Base)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int J = 0;
|
||||||
|
int Stars = 0;
|
||||||
|
for (;;) {
|
||||||
|
Token *T = PeekTok(P, J);
|
||||||
|
if (TokIsOp(T, "*")) {
|
||||||
|
Stars += 1;
|
||||||
|
J++;
|
||||||
|
} else if (TokIsOp(T, "**")) {
|
||||||
|
Stars += 2;
|
||||||
|
J++;
|
||||||
|
} else if (T->Kind == TK_NL) {
|
||||||
|
J++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Stars == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Token *Nxt = PeekTok(P, J);
|
||||||
|
for (size_t I = 0; I < sizeof(EndOps) / sizeof(EndOps[0]); I++) {
|
||||||
|
if (TokIsOp(Nxt, EndOps[I])) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (P->InTypeAnnotation) {
|
||||||
|
if (Nxt->Kind == TK_NEWLINE || Nxt->Kind == TK_NL
|
||||||
|
|| Nxt->Kind == TK_ENDMARKER || TokIsOp(Nxt, ";")) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消费连续的 '*'/'**' 指针后缀('**' 计 2 层),展开为 base | t.CPtr 链。 */
|
||||||
|
static PyObject *ConsumePtrSuffix(Parser *P, PyObject *Node) {
|
||||||
|
for (;;) {
|
||||||
|
Token *T = PeekTok(P, 0);
|
||||||
|
int Layers;
|
||||||
|
if (TokIsOp(T, "*")) {
|
||||||
|
Layers = 1;
|
||||||
|
} else if (TokIsOp(T, "**")) {
|
||||||
|
Layers = 2;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
NextTok(P);
|
||||||
|
P->PtrSuffixTail = T; /* 记录尾 token,供 ParseParen 识别 (T*)x */
|
||||||
|
for (int I = 0; I < Layers; I++) {
|
||||||
|
PyObject *Wrapped = MakePtrType(P, Node, T);
|
||||||
|
Py_DECREF(Node);
|
||||||
|
if (Wrapped == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Node = Wrapped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *ParseCallTail(Parser *P, PyObject *Func, Token *Start);
|
||||||
|
|
||||||
|
/* 判定当前位置的 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用**
|
||||||
|
* (对齐 Python ExprParser._AtPtrSuffixCall):
|
||||||
|
* 1. 节点是类型原子 Name/Attribute/Subscript;
|
||||||
|
* 2. 节点与 '*'/'**' 串**紧贴**(源码字节相邻,无空白/换行);
|
||||||
|
* 3. '*'/'**' 串(串内 token 逐个紧贴)之后**紧贴**一个 '('。
|
||||||
|
* 即整段写作**无空白**的 `T*(x)`(C 风格强转写法)。'(' 不入 EndOps 表
|
||||||
|
* (`a * (b)` 是合法乘法,'(' 可作乘法右操作数起点);故叠加"前驱是类型原子 +
|
||||||
|
* 两侧紧贴",`a * (b + c)` / `f(x) * (y)` / `n * (m - 1)` 等仍按乘法解析。
|
||||||
|
*/
|
||||||
|
static int AtPtrSuffixCall(Parser *P, PyObject *Base) {
|
||||||
|
if (!IsNameOrAttrOrSub(Base)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Token *First = PeekTok(P, 0);
|
||||||
|
Token *Prev = PrevTok(P);
|
||||||
|
if (Prev == NULL || Prev->Kind == TK_NL || Prev->Kind == TK_NEWLINE
|
||||||
|
|| Prev->End != First->Start) {
|
||||||
|
return 0; /* base 与 '*'/'**' 之间有空白/换行 */
|
||||||
|
}
|
||||||
|
int J = 0;
|
||||||
|
int Stars = 0;
|
||||||
|
Token *RunPrev = NULL;
|
||||||
|
Token *Last = NULL;
|
||||||
|
for (;;) {
|
||||||
|
Token *T = PeekTok(P, J);
|
||||||
|
if (TokIsOp(T, "*") || TokIsOp(T, "**")) {
|
||||||
|
if (RunPrev != NULL && T->Start != RunPrev->End) {
|
||||||
|
return 0; /* 星串内部出现空白:非紧邻 */
|
||||||
|
}
|
||||||
|
Stars++;
|
||||||
|
RunPrev = T;
|
||||||
|
Last = T;
|
||||||
|
J++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Stars == 0 || Last == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Token *Nxt = PeekTok(P, J);
|
||||||
|
return TokIsOp(Nxt, "(") && Nxt->Start == Last->End;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消费已认定的 `T*(...)` 强转:func = base 展开的指针类型,随后解析调用实参。
|
||||||
|
* 接管 Base 引用(与 ConsumePtrSuffix 一致),返回 Call 的新引用。 */
|
||||||
|
static PyObject *ConsumePtrSuffixCall(Parser *P, PyObject *Base, Token *Start) {
|
||||||
|
PyObject *Func = ConsumePtrSuffix(P, Base);
|
||||||
|
if (Func == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *Call = ParseCallTail(P, Func, Start);
|
||||||
|
Py_DECREF(Func);
|
||||||
|
return Call;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 类型注解入口:置注解模式标志(对齐 Python ExprParser._ParseTypeAnnotation)。 */
|
||||||
|
static PyObject *ParseTypeAnnotation(Parser *P) {
|
||||||
|
int Saved = P->InTypeAnnotation;
|
||||||
|
P->InTypeAnnotation = 1;
|
||||||
|
PyObject *Node = ParseExpr(P);
|
||||||
|
P->InTypeAnnotation = Saved;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
/* term:factor (*|/|//|%|@ factor)* */
|
/* term:factor (*|/|//|%|@ factor)* */
|
||||||
static PyObject *ParseTerm(Parser *P) {
|
static PyObject *ParseTerm(Parser *P) {
|
||||||
Token *Start = PeekTok(P, 0);
|
Token *Start = PeekTok(P, 0);
|
||||||
@@ -2522,6 +2730,14 @@ static PyObject *ParseTerm(Parser *P) {
|
|||||||
Token *T = PeekTok(P, 0);
|
Token *T = PeekTok(P, 0);
|
||||||
const char *OpName = NULL;
|
const char *OpName = NULL;
|
||||||
if (T->Kind == TK_OP) {
|
if (T->Kind == TK_OP) {
|
||||||
|
if (TokIsOp(T, "*") && AtPtrSuffixEnd(P, Left)) {
|
||||||
|
/* C 风格指针后缀('*' 一层) */
|
||||||
|
Left = ConsumePtrSuffix(P, Left);
|
||||||
|
if (Left == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (TokIsOp(T, "*")) OpName = "Mult";
|
if (TokIsOp(T, "*")) OpName = "Mult";
|
||||||
else if (TokIsOp(T, "/")) OpName = "Div";
|
else if (TokIsOp(T, "/")) OpName = "Div";
|
||||||
else if (TokIsOp(T, "//")) OpName = "FloorDiv";
|
else if (TokIsOp(T, "//")) OpName = "FloorDiv";
|
||||||
@@ -2557,17 +2773,23 @@ static PyObject *ParseTerm(Parser *P) {
|
|||||||
return Left;
|
return Left;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* factor:+|-|~ factor | power */
|
/* factor:+|-|~|*|& factor | power */
|
||||||
static PyObject *ParseFactor(Parser *P) {
|
static PyObject *ParseFactor(Parser *P) {
|
||||||
Token *T = PeekTok(P, 0);
|
Token *T = PeekTok(P, 0);
|
||||||
if (T->Kind == TK_OP && (TokIsOp(T, "+") || TokIsOp(T, "-") || TokIsOp(T, "~"))) {
|
/* 一元前缀与 Python ExprParser._ParseFactor 逐字对齐:'+' USub/'-'/'~' 之外还含
|
||||||
|
* Viper 新规范的 '*'(Deref) 与 '&'(AddrOf)(`*p = v` / `&x`)。缺这两项时
|
||||||
|
* C 解析器对含取址/解引用的文件整体失败并回退纯 Python(C 缺口清单根因)。 */
|
||||||
|
if (T->Kind == TK_OP && (TokIsOp(T, "+") || TokIsOp(T, "-") || TokIsOp(T, "~")
|
||||||
|
|| TokIsOp(T, "*") || TokIsOp(T, "&"))) {
|
||||||
Token *Start = NextTok(P);
|
Token *Start = NextTok(P);
|
||||||
PyObject *Operand = ParseFactor(P);
|
PyObject *Operand = ParseFactor(P);
|
||||||
if (Operand == NULL) {
|
if (Operand == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
const char *OpName = TokIsOp(Start, "+") ? "UAdd"
|
const char *OpName = TokIsOp(Start, "+") ? "UAdd"
|
||||||
: (TokIsOp(Start, "-") ? "USub" : "Invert");
|
: (TokIsOp(Start, "-") ? "USub"
|
||||||
|
: (TokIsOp(Start, "~") ? "Invert"
|
||||||
|
: (TokIsOp(Start, "*") ? "Deref" : "AddrOf")));
|
||||||
PyObject *Op = MakeSingleton(P, OpName);
|
PyObject *Op = MakeSingleton(P, OpName);
|
||||||
if (Op == NULL) {
|
if (Op == NULL) {
|
||||||
Py_DECREF(Operand);
|
Py_DECREF(Operand);
|
||||||
@@ -2595,6 +2817,10 @@ static PyObject *ParsePower(Parser *P) {
|
|||||||
if (!AtOp(P, "**")) {
|
if (!AtOp(P, "**")) {
|
||||||
return Base;
|
return Base;
|
||||||
}
|
}
|
||||||
|
if (AtPtrSuffixEnd(P, Base)) {
|
||||||
|
/* C 风格指针后缀('**' 计 2 层) */
|
||||||
|
return ConsumePtrSuffix(P, Base);
|
||||||
|
}
|
||||||
NextTok(P);
|
NextTok(P);
|
||||||
PyObject *Exp = ParseFactor(P);
|
PyObject *Exp = ParseFactor(P);
|
||||||
if (Exp == NULL) {
|
if (Exp == NULL) {
|
||||||
@@ -2946,7 +3172,15 @@ static PyObject *ParsePrimary(Parser *P) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
SkipNL(P);
|
SkipNL(P);
|
||||||
Token *T = PeekTok(P, 0);
|
Token *T = PeekTok(P, 0);
|
||||||
if (T->Kind == TK_OP && TokIsOp(T, ".")) {
|
if (T->Kind == TK_OP && (TokIsOp(T, "*") || TokIsOp(T, "**"))
|
||||||
|
&& AtPtrSuffixCall(P, Node)) {
|
||||||
|
/* `T*(x)` / `T**(x)` 指针后缀强转:解析为 cast 调用(与 (T | t.CPtr)(x)
|
||||||
|
* 同形)。在 trailer 循环里消费,故其后的 .attr / [i] / (…) 继续挂接。 */
|
||||||
|
Node = ConsumePtrSuffixCall(P, Node, Start);
|
||||||
|
if (Node == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else if (T->Kind == TK_OP && TokIsOp(T, ".")) {
|
||||||
NextTok(P);
|
NextTok(P);
|
||||||
Token *AttrTok = ExpectName(P, NULL);
|
Token *AttrTok = ExpectName(P, NULL);
|
||||||
if (AttrTok == NULL) {
|
if (AttrTok == NULL) {
|
||||||
@@ -3032,6 +3266,26 @@ static PyObject *ParseAtom(Parser *P) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 无括号强转 `(T*)x` 的操作数起点判据(对齐 Python ExprParser._AtCastOperand):
|
||||||
|
* NAME/NUMBER/STRING,或 OP 中的 '['/'{'/'*'/'&'/'-'/'+'/'~'。'(' 不在内
|
||||||
|
* (`(T*)(x)` 已由调用 trailer 处理);NL 不跳过(换行后是新语句,不是操作数)。 */
|
||||||
|
static int AtCastOperand(Parser *P) {
|
||||||
|
Token *T = PeekTok(P, 0);
|
||||||
|
if (T->Kind == TK_NAME || T->Kind == TK_NUMBER || T->Kind == TK_STRING) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (T->Kind != TK_OP) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static const char *Ops[] = {"[", "{", "*", "&", "-", "+", "~"};
|
||||||
|
for (size_t I = 0; I < sizeof(Ops) / sizeof(Ops[0]); I++) {
|
||||||
|
if (TokIsOp(T, Ops[I])) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 括号:空元组 / yield / walrus / 生成器表达式 / 元组 / 普通表达式 */
|
/* 括号:空元组 / yield / walrus / 生成器表达式 / 元组 / 普通表达式 */
|
||||||
static PyObject *ParseParen(Parser *P) {
|
static PyObject *ParseParen(Parser *P) {
|
||||||
Token *Start = NextTok(P); /* '(' */
|
Token *Start = NextTok(P); /* '(' */
|
||||||
@@ -3101,6 +3355,10 @@ static PyObject *ParseParen(Parser *P) {
|
|||||||
if (First == NULL) {
|
if (First == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* 无括号强转 `(T*)x`:内层表达式以指针后缀结束(_Prev() 即该后缀尾 token),
|
||||||
|
* ')' 紧随其后 → 与 `(T*)(x)` / `T*(x)` 同形,产物为 Call(func = T | t.CPtr)。 */
|
||||||
|
int IsPtrCast = (P->PtrSuffixTail != NULL && AtOp(P, ")")
|
||||||
|
&& PrevTok(P) == P->PtrSuffixTail);
|
||||||
SkipNL(P);
|
SkipNL(P);
|
||||||
if (AtName(P, "for")) {
|
if (AtName(P, "for")) {
|
||||||
PyObject *Generators = ParseComprehensionGenerators(P);
|
PyObject *Generators = ParseComprehensionGenerators(P);
|
||||||
@@ -3170,6 +3428,33 @@ static PyObject *ParseParen(Parser *P) {
|
|||||||
Py_DECREF(First);
|
Py_DECREF(First);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (IsPtrCast && AtCastOperand(P)) {
|
||||||
|
PyObject *Operand = ParseFactor(P);
|
||||||
|
if (Operand == NULL) {
|
||||||
|
Py_DECREF(First);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *Args = NewList();
|
||||||
|
PyObject *Kw = NewList();
|
||||||
|
if (Args == NULL || Kw == NULL) {
|
||||||
|
Py_XDECREF(Args);
|
||||||
|
Py_XDECREF(Kw);
|
||||||
|
Py_DECREF(Operand);
|
||||||
|
Py_DECREF(First);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyList_Append(Args, Operand);
|
||||||
|
Py_DECREF(Operand);
|
||||||
|
PyObject *Argv[3] = { First, Args, Kw };
|
||||||
|
PyObject *Call = NewNode(P, "Call", Argv, 3);
|
||||||
|
Py_DECREF(Args);
|
||||||
|
Py_DECREF(Kw);
|
||||||
|
Py_DECREF(First);
|
||||||
|
if (Call != NULL) {
|
||||||
|
FixLoc(P, Call, Start);
|
||||||
|
}
|
||||||
|
return Call;
|
||||||
|
}
|
||||||
return First;
|
return First;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4012,7 +4297,7 @@ static PyObject *ParseArg(Parser *P, int AllowAnnotation) {
|
|||||||
PyObject *Annotation = NULL;
|
PyObject *Annotation = NULL;
|
||||||
if (AllowAnnotation && AtOp(P, ":")) {
|
if (AllowAnnotation && AtOp(P, ":")) {
|
||||||
NextTok(P);
|
NextTok(P);
|
||||||
Annotation = ParseExpr(P);
|
Annotation = ParseTypeAnnotation(P);
|
||||||
if (Annotation == NULL) {
|
if (Annotation == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -4037,6 +4322,7 @@ static PyObject *ParseArg(Parser *P, int AllowAnnotation) {
|
|||||||
static PyObject *ParseStatement(Parser *P);
|
static PyObject *ParseStatement(Parser *P);
|
||||||
static PyObject *ParseSimpleStmt(Parser *P);
|
static PyObject *ParseSimpleStmt(Parser *P);
|
||||||
static PyObject *ParseSuite(Parser *P);
|
static PyObject *ParseSuite(Parser *P);
|
||||||
|
static PyObject *ParseSuiteOwned(Parser *P, int OwnerIsClass);
|
||||||
static PyObject *ParseIf(Parser *P);
|
static PyObject *ParseIf(Parser *P);
|
||||||
static PyObject *ParseWhile(Parser *P);
|
static PyObject *ParseWhile(Parser *P);
|
||||||
static PyObject *ParseFor(Parser *P, int IsAsync);
|
static PyObject *ParseFor(Parser *P, int IsAsync);
|
||||||
@@ -4056,6 +4342,7 @@ static PyObject *ParseGlobal(Parser *P, const char *NodeName);
|
|||||||
static PyObject *ParseDelete(Parser *P);
|
static PyObject *ParseDelete(Parser *P);
|
||||||
static PyObject *ParseAssert(Parser *P);
|
static PyObject *ParseAssert(Parser *P);
|
||||||
static PyObject *ParseDecorated(Parser *P);
|
static PyObject *ParseDecorated(Parser *P);
|
||||||
|
static PyObject *ParseVirtualModified(Parser *P, PyObject *Decorators);
|
||||||
static PyObject *ParseTypeParams(Parser *P);
|
static PyObject *ParseTypeParams(Parser *P);
|
||||||
static PyObject *ParseExceptHandler(Parser *P);
|
static PyObject *ParseExceptHandler(Parser *P);
|
||||||
static PyObject *ParseTargetList(Parser *P);
|
static PyObject *ParseTargetList(Parser *P);
|
||||||
@@ -4116,6 +4403,9 @@ static PyObject *ParseStatement(Parser *P) {
|
|||||||
if (TokIsName(T, "define") && PeekTok(P, 1)->Kind == TK_NAME) {
|
if (TokIsName(T, "define") && PeekTok(P, 1)->Kind == TK_NAME) {
|
||||||
return ParseFunctionDef(P, 0, NULL);
|
return ParseFunctionDef(P, 0, NULL);
|
||||||
}
|
}
|
||||||
|
if (TokIsName(T, "virtual") || TokIsName(T, "novirtual")) {
|
||||||
|
return ParseVirtualModified(P, NULL);
|
||||||
|
}
|
||||||
if ((TokIsName(T, "fn") || TokIsName(T, "fun") || TokIsName(T, "func")
|
if ((TokIsName(T, "fn") || TokIsName(T, "fun") || TokIsName(T, "func")
|
||||||
|| TokIsName(T, "function"))
|
|| TokIsName(T, "function"))
|
||||||
&& PeekTok(P, 1)->Kind == TK_NAME) {
|
&& PeekTok(P, 1)->Kind == TK_NAME) {
|
||||||
@@ -4361,7 +4651,7 @@ static PyObject *TryParseAnnAssign(Parser *P) {
|
|||||||
if (T0->Kind == TK_NAME && T1->Kind == TK_OP && TokIsOp(T1, ":")) {
|
if (T0->Kind == TK_NAME && T1->Kind == TK_OP && TokIsOp(T1, ":")) {
|
||||||
Token *NameTok = NextTok(P);
|
Token *NameTok = NextTok(P);
|
||||||
NextTok(P); /* ':' */
|
NextTok(P); /* ':' */
|
||||||
PyObject *Annotation = ParseExpr(P);
|
PyObject *Annotation = ParseTypeAnnotation(P);
|
||||||
if (Annotation == NULL) {
|
if (Annotation == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -4406,7 +4696,7 @@ static PyObject *TryParseAnnAssign(Parser *P) {
|
|||||||
}
|
}
|
||||||
if (AtOp(P, ":") && IsNameOrAttrOrSub(Expr)) {
|
if (AtOp(P, ":") && IsNameOrAttrOrSub(Expr)) {
|
||||||
NextTok(P);
|
NextTok(P);
|
||||||
PyObject *Annotation = ParseExpr(P);
|
PyObject *Annotation = ParseTypeAnnotation(P);
|
||||||
if (Annotation == NULL) {
|
if (Annotation == NULL) {
|
||||||
Py_DECREF(Expr);
|
Py_DECREF(Expr);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -4848,11 +5138,15 @@ static PyObject *ParseDecorated(Parser *P) {
|
|||||||
SkipNEWLINE(P);
|
SkipNEWLINE(P);
|
||||||
}
|
}
|
||||||
Token *T = PeekTok(P, 0);
|
Token *T = PeekTok(P, 0);
|
||||||
|
/* extern / export 必须走 ParseExternDef(与 Python _ParseDecorated 的
|
||||||
|
* _ParseExternDef/_ParseExportDef 分派一致)。此前误走 ParseFunctionDef:
|
||||||
|
* 它无条件把当前 token 当 `def` 吃掉、再 ExpectName 拿到 `def`,
|
||||||
|
* 于是 `@deco` + `export def f(...)` 在 `(` 处报 expected operator NAME('f')。 */
|
||||||
if (T->Kind == TK_NAME && TokIsName(T, "extern")) {
|
if (T->Kind == TK_NAME && TokIsName(T, "extern")) {
|
||||||
return ParseFunctionDef(P, 0, DecoratorList);
|
return ParseExternDef(P, 0, DecoratorList);
|
||||||
}
|
}
|
||||||
if (T->Kind == TK_NAME && TokIsName(T, "export")) {
|
if (T->Kind == TK_NAME && TokIsName(T, "export")) {
|
||||||
return ParseFunctionDef(P, 0, DecoratorList);
|
return ParseExternDef(P, 1, DecoratorList);
|
||||||
}
|
}
|
||||||
if (T->Kind == TK_NAME && TokIsName(T, "define")) {
|
if (T->Kind == TK_NAME && TokIsName(T, "define")) {
|
||||||
return ParseFunctionDef(P, 0, DecoratorList);
|
return ParseFunctionDef(P, 0, DecoratorList);
|
||||||
@@ -4867,11 +5161,67 @@ static PyObject *ParseDecorated(Parser *P) {
|
|||||||
if (T->Kind == TK_NAME && TokIsName(T, "class")) {
|
if (T->Kind == TK_NAME && TokIsName(T, "class")) {
|
||||||
return ParseClassDef(P, DecoratorList);
|
return ParseClassDef(P, DecoratorList);
|
||||||
}
|
}
|
||||||
|
if (T->Kind == TK_NAME && (TokIsName(T, "virtual") || TokIsName(T, "novirtual"))) {
|
||||||
|
return ParseVirtualModified(P, DecoratorList);
|
||||||
|
}
|
||||||
SyntaxErrorAt(P, T, "decorator must be followed by def/class");
|
SyntaxErrorAt(P, T, "decorator must be followed by def/class");
|
||||||
Py_DECREF(DecoratorList);
|
Py_DECREF(DecoratorList);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* virtual / novirtual 修饰符(对齐 vast Parser._ParseVirtualModified):
|
||||||
|
* 语句起始的 `virtual`/`novirtual` 后随 class/def(def 别名 fn/fun/func/function 同样接受)
|
||||||
|
* 时,合成装饰器 t.CVTable / t.NoVTable 追加到既有装饰器之后,再交由
|
||||||
|
* ParseClassDef / ParseFunctionDef 解析。非法形态(后随非 class/def;virtual def
|
||||||
|
* 不在类体内)一律报错,绝不静默丢弃修饰符。 */
|
||||||
|
static PyObject *ParseVirtualModified(Parser *P, PyObject *Decorators) {
|
||||||
|
Token *Mod = PeekTok(P, 0);
|
||||||
|
Token *Nxt = PeekTok(P, 1);
|
||||||
|
const char *Marker = TokIsName(Mod, "virtual") ? "CVTable" : "NoVTable";
|
||||||
|
int IsClass = 0;
|
||||||
|
int IsDef = 0;
|
||||||
|
if (Nxt->Kind == TK_NAME) {
|
||||||
|
if (TokIsName(Nxt, "class")) {
|
||||||
|
IsClass = 1;
|
||||||
|
} else if (TokIsName(Nxt, "def") || TokIsName(Nxt, "fn")
|
||||||
|
|| TokIsName(Nxt, "fun") || TokIsName(Nxt, "func")
|
||||||
|
|| TokIsName(Nxt, "function")) {
|
||||||
|
IsDef = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!IsClass && !IsDef) {
|
||||||
|
SyntaxErrorAt(P, Mod, "virtual/novirtual must precede class/def");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (IsDef && !P->InClassBody) {
|
||||||
|
SyntaxErrorAt(P, Mod, "virtual def must appear inside a class body");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
NextTok(P); /* 消费修饰符 */
|
||||||
|
PyObject *Deco = MakeTAttrNode(P, Marker);
|
||||||
|
if (Deco == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
FixLoc(P, Deco, Mod);
|
||||||
|
PyObject *DecoList = NewList();
|
||||||
|
if (DecoList == NULL) {
|
||||||
|
Py_DECREF(Deco);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (Decorators != NULL) {
|
||||||
|
Py_ssize_t N = PyList_GET_SIZE(Decorators);
|
||||||
|
for (Py_ssize_t I = 0; I < N; I++) {
|
||||||
|
PyList_Append(DecoList, PyList_GET_ITEM(Decorators, I));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PyList_Append(DecoList, Deco);
|
||||||
|
Py_DECREF(Deco);
|
||||||
|
PyObject *Node = IsClass ? ParseClassDef(P, DecoList)
|
||||||
|
: ParseFunctionDef(P, 0, DecoList);
|
||||||
|
Py_DECREF(DecoList);
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
/* def / fn / fun / func / function(含 async def) */
|
/* def / fn / fun / func / function(含 async def) */
|
||||||
static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators) {
|
static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators) {
|
||||||
Token *Start = NextTok(P);
|
Token *Start = NextTok(P);
|
||||||
@@ -4903,7 +5253,7 @@ static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators)
|
|||||||
PyObject *Returns = NULL;
|
PyObject *Returns = NULL;
|
||||||
if (AtOp(P, "->")) {
|
if (AtOp(P, "->")) {
|
||||||
NextTok(P);
|
NextTok(P);
|
||||||
Returns = ParseExpr(P);
|
Returns = ParseTypeAnnotation(P);
|
||||||
if (Returns == NULL) {
|
if (Returns == NULL) {
|
||||||
Py_DECREF(Args);
|
Py_DECREF(Args);
|
||||||
Py_DECREF(TypeParams);
|
Py_DECREF(TypeParams);
|
||||||
@@ -5054,7 +5404,7 @@ static PyObject *ParseExternDef(Parser *P, int IsExport, PyObject *Decorators) {
|
|||||||
PyObject *Returns = NULL;
|
PyObject *Returns = NULL;
|
||||||
if (AtOp(P, "->")) {
|
if (AtOp(P, "->")) {
|
||||||
NextTok(P);
|
NextTok(P);
|
||||||
Returns = ParseExpr(P);
|
Returns = ParseTypeAnnotation(P);
|
||||||
if (Returns == NULL) {
|
if (Returns == NULL) {
|
||||||
Py_DECREF(Args);
|
Py_DECREF(Args);
|
||||||
Py_DECREF(TypeParams);
|
Py_DECREF(TypeParams);
|
||||||
@@ -5335,7 +5685,7 @@ static PyObject *ParseClassDef(Parser *P, PyObject *Decorators) {
|
|||||||
Py_DECREF(TypeParams);
|
Py_DECREF(TypeParams);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyObject *Body = ParseSuite(P);
|
PyObject *Body = ParseSuiteOwned(P, 1);
|
||||||
if (Body == NULL) {
|
if (Body == NULL) {
|
||||||
Py_DECREF(Bases);
|
Py_DECREF(Bases);
|
||||||
Py_DECREF(Keywords);
|
Py_DECREF(Keywords);
|
||||||
@@ -5381,7 +5731,7 @@ static PyObject *ParseClassDef(Parser *P, PyObject *Decorators) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 语句块(套件) */
|
/* 语句块(套件) */
|
||||||
static PyObject *ParseSuite(Parser *P) {
|
static PyObject *ParseSuiteImpl(Parser *P) {
|
||||||
if (PeekTok(P, 0)->Kind == TK_NEWLINE) {
|
if (PeekTok(P, 0)->Kind == TK_NEWLINE) {
|
||||||
NextTok(P);
|
NextTok(P);
|
||||||
while (PeekTok(P, 0)->Kind == TK_NL) {
|
while (PeekTok(P, 0)->Kind == TK_NL) {
|
||||||
@@ -5458,9 +5808,25 @@ static PyObject *ParseSuite(Parser *P) {
|
|||||||
Py_DECREF(Body);
|
Py_DECREF(Body);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* 吞掉单行套件后的空行/注释行 NL:与纯 Python _ParseSuite 的单行分支一致,
|
||||||
|
* 使外层语句的 end_lineno 落在最后一个被吞掉的 NL 上(而非本行 NEWLINE)。 */
|
||||||
|
SkipNL(P);
|
||||||
return Body;
|
return Body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 套件入口:OwnerIsClass 表示该套件的直接拥有者是否 ClassDef(供 virtual def 判据)。 */
|
||||||
|
static PyObject *ParseSuiteOwned(Parser *P, int OwnerIsClass) {
|
||||||
|
int Prev = P->InClassBody;
|
||||||
|
P->InClassBody = OwnerIsClass;
|
||||||
|
PyObject *Body = ParseSuiteImpl(P);
|
||||||
|
P->InClassBody = Prev;
|
||||||
|
return Body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *ParseSuite(Parser *P) {
|
||||||
|
return ParseSuiteOwned(P, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* if / elif / else */
|
/* if / elif / else */
|
||||||
static PyObject *ParseIf(Parser *P) {
|
static PyObject *ParseIf(Parser *P) {
|
||||||
Token *Start = NextTok(P); /* if */
|
Token *Start = NextTok(P); /* if */
|
||||||
@@ -6492,13 +6858,40 @@ static PyObject *VastC_Parse(PyObject *Self, PyObject *Args, PyObject *Kwargs) {
|
|||||||
P.Error = 0;
|
P.Error = 0;
|
||||||
P.ErrMsg = NULL;
|
P.ErrMsg = NULL;
|
||||||
P.ErrLine = 0;
|
P.ErrLine = 0;
|
||||||
|
P.ErrTok = NULL;
|
||||||
P.FStringExprEnd = 0;
|
P.FStringExprEnd = 0;
|
||||||
|
P.InTypeAnnotation = 0;
|
||||||
|
P.InClassBody = 0;
|
||||||
|
P.PtrSuffixTail = NULL;
|
||||||
PyObject *Tree = ParseModule(&P);
|
PyObject *Tree = ParseModule(&P);
|
||||||
PyMem_Free(Tokens);
|
PyMem_Free(Tokens);
|
||||||
PyMem_Free(LineStarts);
|
PyMem_Free(LineStarts);
|
||||||
if (Tree == NULL) {
|
if (Tree == NULL) {
|
||||||
if (P.Error) {
|
if (P.Error) {
|
||||||
PyErr_SetString(PyExc_SyntaxError, P.ErrMsg ? P.ErrMsg : "vast_c: syntax error");
|
/* 报错必须自足可定位:带上 类型名 + token 原文 + 行号,措辞与
|
||||||
|
* Python ExprParser 的 `unexpected token OP('*') at line N` 一致,
|
||||||
|
* 这样 `[vast] C 解析器失败,回退纯 Python:<文件>: …` 一行即可定位。 */
|
||||||
|
char TokBuf[64];
|
||||||
|
const char *TokName = "ENDMARKER";
|
||||||
|
TokBuf[0] = '\0';
|
||||||
|
if (P.ErrTok != NULL) {
|
||||||
|
TokName = TokKindName(P.ErrTok->Kind);
|
||||||
|
Py_ssize_t TL = (Py_ssize_t)(P.ErrTok->End - P.ErrTok->Start);
|
||||||
|
if (TL < 0) {
|
||||||
|
TL = 0;
|
||||||
|
}
|
||||||
|
if (TL > (Py_ssize_t)sizeof(TokBuf) - 1) {
|
||||||
|
TL = (Py_ssize_t)sizeof(TokBuf) - 1;
|
||||||
|
}
|
||||||
|
for (Py_ssize_t I = 0; I < TL; I++) {
|
||||||
|
char C = P.ErrTok->Start[I];
|
||||||
|
TokBuf[I] = (C == '\n' || C == '\r') ? ' ' : C;
|
||||||
|
}
|
||||||
|
TokBuf[TL] = '\0';
|
||||||
|
}
|
||||||
|
PyErr_Format(PyExc_SyntaxError, "%s %s('%s') at line %d",
|
||||||
|
P.ErrMsg ? P.ErrMsg : "vast_c: syntax error",
|
||||||
|
TokName, TokBuf, P.ErrLine);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user