审计并修复 23 个文件:类型注解中的 T1 | T2 一律 fail-fast;typedef/define/export/extern/static/const/volatile/attribute 优先读取 Markers/Attributes/ByteOrder/BitWidth;指针统一 CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr Subscript;stub 输出统一新语法。
1223 lines
61 KiB
Python
1223 lines
61 KiB
Python
"""StubGen 转换器模块 - 将 Python 文件转换为存根格式"""
|
||
from __future__ import annotations
|
||
import vast as ast
|
||
import re
|
||
import fnmatch
|
||
from typing import List, Dict
|
||
from lib.core.SymbolUtils import IsListAnnotation, ExpandSoftKeywords
|
||
|
||
|
||
# 模块级转换结果缓存:{ (PyContent, ModuleName): str }
|
||
# convert 是纯函数(类级配置 IncludeAliasMap/VarExcludeList/MacroExcludeList 运行期不变),
|
||
# 相同输入必然产生相同输出。结果字符串不可变,调用方仅将其写入文件或拼接出新字符串,
|
||
# 缓存结果可安全复用(无需深拷贝)。Set* 配置方法会清空缓存以防配置变化导致结果过期。
|
||
_ConvertResultCache: dict[tuple[str, str], str] = {}
|
||
_ConvertCacheMaxSize: int = 4096
|
||
|
||
# 提取 import 语句行尾注释(`_GetImportString` / `_GetImportFromString`)用的
|
||
# **预编译**正则(性能:字面量正则预编译避免 re 缓存查找与编译开销)。
|
||
_RE_LINE_COMMENT: re.Pattern[str] = re.compile(r'#.*$')
|
||
|
||
|
||
_STUB_TYPE_ALIASES: dict[str, str] = {
|
||
'CPtr': 'rawptr', 'CArrayPtr': 'rawptr', 'CAutoPtr': 'rawptr',
|
||
'CRef': 'rawptr', 'CTempRef': 'rawptr',
|
||
# 旧内建类型名 → 新语法内建名(输入兼容;输出不得再出现 C* 标记)
|
||
'CChar': 'char', 'CInt': 'int', 'CShort': 'short', 'CLong': 'long',
|
||
'CLongLong': 'longlong', 'CFloat': 'float', 'CDouble': 'double',
|
||
'CVoid': 'void', 'CUnsigned': 'uint', 'CUnsignedChar': 'uchar',
|
||
'CUnsignedInt': 'uint', 'CUnsignedShort': 'ushort',
|
||
'CUnsignedLong': 'ulong', 'CUnsignedLongLong': 'ulonglong',
|
||
'CSignedChar': 'char', 'CBool': 'bool', 'CSizeT': 'sizet',
|
||
'CInt8T': 'int8', 'CInt16T': 'int16', 'CInt32T': 'int32', 'CInt64T': 'int64',
|
||
'CUInt8T': 'uint8', 'CUInt16T': 'uint16', 'CUInt32T': 'uint32', 'CUInt64T': 'uint64',
|
||
'CIntPtrT': 'intptr', 'CUIntPtrT': 'uintptr', 'CPtrDiffT': 'ptrdifft',
|
||
'CWCharT': 'wchar', 'CChar8T': 'char8', 'CChar16T': 'char16', 'CChar32T': 'char32',
|
||
'CFloat8T': 'float8', 'CFloat16T': 'float16', 'CFloat32T': 'float32',
|
||
'CFloat64T': 'float64', 'CFloat128T': 'float128',
|
||
}
|
||
|
||
|
||
class PythonToStubConverter:
|
||
"""将 Python 文件转换为存根格式"""
|
||
|
||
# __include 别名映射表 {alias: ModuleName}
|
||
IncludeAliasMap: Dict[str, str] = {}
|
||
|
||
# 变量排除列表 - 匹配这些模式的变量不会被添加到存根文件
|
||
VarExcludeList: List[str] = []
|
||
|
||
# 宏排除列表 - 匹配这些模式的宏不会被添加到存根文件
|
||
MacroExcludeList: List[str] = []
|
||
|
||
@classmethod
|
||
def SetIncludeAliasMap(cls, alias_map: Dict[str, str]) -> None:
|
||
"""设置 __include 别名映射表"""
|
||
cls.IncludeAliasMap = alias_map
|
||
_ConvertResultCache.clear()
|
||
|
||
@classmethod
|
||
def SetVarExcludeList(cls, exclude_list: List[str]) -> None:
|
||
"""设置变量排除列表"""
|
||
cls.VarExcludeList = exclude_list
|
||
_ConvertResultCache.clear()
|
||
|
||
@classmethod
|
||
def SetMacroExcludeList(cls, exclude_list: List[str]) -> None:
|
||
"""设置宏排除列表"""
|
||
cls.MacroExcludeList = exclude_list
|
||
_ConvertResultCache.clear()
|
||
|
||
@classmethod
|
||
def _ShouldExcludeVar(cls, VarName: str) -> bool:
|
||
"""检查变量是否应该被排除"""
|
||
for pattern in cls.VarExcludeList:
|
||
if fnmatch.fnmatch(VarName, pattern):
|
||
return True
|
||
return False
|
||
|
||
@classmethod
|
||
def _ShouldExcludeMacro(cls, MacroName: str) -> bool:
|
||
"""检查宏是否应该被排除"""
|
||
for pattern in cls.MacroExcludeList:
|
||
if fnmatch.fnmatch(MacroName, pattern):
|
||
return True
|
||
return False
|
||
|
||
@staticmethod
|
||
def convert(PyContent: str, ModuleName: str) -> str:
|
||
"""将 Python 代码转换为存根格式"""
|
||
# 软关键字展开:fn/fun/func/function -> def(行内等宽/更短,行号不变),
|
||
# 保证后续 ast.parse 与 SourceLines 回读行号一致。
|
||
PyContent = ExpandSoftKeywords(PyContent)
|
||
|
||
# 模块级缓存:同一 (内容, 模块名) 直接复用上次转换结果(纯函数输出,不可变字符串)
|
||
CacheKey: tuple[str, str] = (PyContent, ModuleName)
|
||
CachedResult: str | None = _ConvertResultCache.get(CacheKey)
|
||
if CachedResult is not None:
|
||
return CachedResult
|
||
|
||
lines: list[str] = PyContent.split('\n')
|
||
StubLines: list[str] = []
|
||
|
||
# 添加文件头
|
||
StubLines.append('"""')
|
||
StubLines.append(f'Auto-generated Python stub file from {ModuleName}.vp')
|
||
StubLines.append(f'Module: {ModuleName}')
|
||
StubLines.append('"""')
|
||
StubLines.append('')
|
||
|
||
# 解析 Python 代码,供下方主循环复用(避免重复 parse)。
|
||
# stub 头部不注入 import 行;内建标记按裸名使用,无需导入。
|
||
tree: ast.Module | None = None
|
||
try:
|
||
# 传来源名:解析失败时 vast 的报错/回退诊断才带得上模块名(否则记为 <unknown>)
|
||
tree = ast.parse(PyContent, f'{ModuleName}.vp')
|
||
except SyntaxError:
|
||
# 语法错误:同一内容两次解析结果必然相同,与主循环 fail-fast 行为一致
|
||
raise
|
||
except Exception:
|
||
# 非语法类解析异常:保持原语义(第一遍吞掉,主循环重新解析后向上传播)
|
||
tree = None
|
||
|
||
# 解析 Python 代码,提取类型定义(复用上方的 parse 结果)
|
||
try:
|
||
if tree is None:
|
||
# 第一遍解析失败(非语法类异常):重试一次,保持原"主循环重新解析"语义
|
||
tree = ast.parse(PyContent, f'{ModuleName}.vp')
|
||
|
||
# 第一遍扫描:收集 Postdefinition 变量
|
||
PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]}
|
||
for node in tree.body:
|
||
if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
|
||
TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation)
|
||
# 检查是否是 Postdefinition 类型
|
||
if 'Postdefinition' in TypeStr:
|
||
# 提取引用的类名
|
||
ClassName: str | None = PythonToStubConverter._ExtractPostdefClass(node.annotation)
|
||
if ClassName:
|
||
if ClassName not in PostdefVars:
|
||
PostdefVars[ClassName] = []
|
||
PostdefVars[ClassName].append((node.target.id, TypeStr, node))
|
||
|
||
# 按原始顺序处理节点,保持代码的原始顺序
|
||
PrevType: str | None = None # 记录前一个节点的类型
|
||
|
||
for node in tree.body:
|
||
CurrentType: str | None = None
|
||
|
||
if isinstance(node, ast.ClassDef):
|
||
CurrentType = 'class'
|
||
# 如果前一个不是类,添加空行分隔
|
||
if PrevType is not None and PrevType != 'class':
|
||
StubLines.append('')
|
||
# 检查是否有对应的 Postdefinition 变量
|
||
ClassPostdefs: list[tuple[str, str, ast.AnnAssign]] = PostdefVars.get(node.name, [])
|
||
ClassLines: list[str] = PythonToStubConverter._ConvertClass(
|
||
node, SourceLines=lines, PostdefVars=ClassPostdefs
|
||
)
|
||
StubLines.extend(ClassLines)
|
||
elif isinstance(node, ast.FunctionDef):
|
||
CurrentType = 'function'
|
||
# 如果前一个是类或者是第一个函数,添加空行分隔
|
||
if PrevType == 'class' or (PrevType is not None and PrevType != 'function'):
|
||
StubLines.append('')
|
||
FuncLines: list[str] = PythonToStubConverter._ConvertFunction(node, SourceLines=lines)
|
||
StubLines.extend(FuncLines)
|
||
StubLines.append('') # 函数后空行
|
||
elif isinstance(node, ast.AnnAssign):
|
||
CurrentType = 'variable'
|
||
# 跳过 Postdefinition 变量(已经在类中处理)
|
||
TypeStr = PythonToStubConverter._GetTypeString(node.annotation)
|
||
if 'Postdefinition' in TypeStr:
|
||
continue
|
||
# 检查是否带有 static 标志(优先读 Markers),如果有则不排除
|
||
_AnnMarkers: tuple[str, ...] = (
|
||
tuple(getattr(node, 'Markers', None) or ())
|
||
+ tuple(getattr(node.annotation, 'Markers', None) or ()))
|
||
HasStatic: bool = 'static' in _AnnMarkers or 'CStatic' in TypeStr
|
||
# 检查变量是否应该被排除
|
||
ShouldExclude: bool = False
|
||
if isinstance(node.target, ast.Name) and not HasStatic:
|
||
VarName: str = node.target.id
|
||
if PythonToStubConverter._ShouldExcludeVar(VarName):
|
||
ShouldExclude = True
|
||
if ShouldExclude:
|
||
continue
|
||
# 如果前一个不是变量,添加空行分隔
|
||
if PrevType is not None and PrevType != 'variable':
|
||
StubLines.append('')
|
||
VarLines: list[str] = PythonToStubConverter._ConvertVariable(node, SourceLines=lines)
|
||
# 移除多余的空行
|
||
VarLines = [line for line in VarLines if line.strip()]
|
||
StubLines.extend(VarLines)
|
||
elif isinstance(node, ast.Import):
|
||
# 处理导入语句
|
||
CurrentType = 'import'
|
||
ImportStr: str = PythonToStubConverter._GetImportString(node, SourceLines=lines)
|
||
if ImportStr:
|
||
if PrevType is not None and PrevType != 'import':
|
||
StubLines.append('')
|
||
StubLines.append(ImportStr)
|
||
elif isinstance(node, ast.Assign):
|
||
CurrentType = 'variable'
|
||
# 处理模块级别的全局变量(无类型标注)
|
||
if PrevType is not None and PrevType != 'variable':
|
||
StubLines.append('')
|
||
AssignLines: list[str] = PythonToStubConverter._ConvertGlobalAssign(node)
|
||
StubLines.extend(AssignLines)
|
||
elif isinstance(node, ast.ImportFrom):
|
||
# 处理 from ... import ... 语句
|
||
CurrentType = 'import'
|
||
ImportStr = PythonToStubConverter._GetImportFromString(node, SourceLines=lines)
|
||
if ImportStr:
|
||
if PrevType is not None and PrevType != 'import':
|
||
StubLines.append('')
|
||
StubLines.append(ImportStr)
|
||
elif isinstance(node, ast.Expr):
|
||
# 处理模块级别的宏调用,如 CUndef(...) / CPragma(...)
|
||
ExprStr: str = PythonToStubConverter._GetExprString(node.value)
|
||
if ExprStr:
|
||
CurrentType = 'macro'
|
||
StubLines.append(ExprStr)
|
||
StubLines.append('') # 宏后空行
|
||
elif isinstance(node, ast.If):
|
||
# 处理模块级别的 cif 宏条件,如 `cif FF_MULTI_PARTITION:`
|
||
IfStr: str = PythonToStubConverter._GetModuleIfMacroString(node)
|
||
if IfStr:
|
||
CurrentType = 'macro'
|
||
StubLines.append(IfStr)
|
||
StubLines.append('') # 宏后空行
|
||
|
||
if CurrentType:
|
||
PrevType = CurrentType
|
||
|
||
except SyntaxError as e:
|
||
# 语法错误直接终止(fail-fast),不允许生成残缺 stub 被全局缓存命中。
|
||
# 残缺 vpi 只含 import 头,函数/类型声明全缺,会在后续阶段以
|
||
# undefined symbol / mangle 错误形式爆发(如 ExpandKeywordsTest 链接失败)。
|
||
raise
|
||
|
||
Result: str = '\n'.join(StubLines)
|
||
# 缓存上限保护:条目过多时整体清空,避免内存无限增长(结果可随时重算)
|
||
if len(_ConvertResultCache) >= _ConvertCacheMaxSize:
|
||
_ConvertResultCache.clear()
|
||
_ConvertResultCache[CacheKey] = Result
|
||
return Result
|
||
|
||
@staticmethod
|
||
def _ConvertClass(node: ast.ClassDef, SourceLines: list[str] | None = None, PostdefVars: list[tuple[str, str, ast.AnnAssign]] | None = None, indent: int = 0) -> list[str]:
|
||
"""转换类定义
|
||
|
||
Args:
|
||
node: 类定义节点
|
||
SourceLines: 源代码行列表
|
||
PostdefVars: Postdefinition 变量列表 [(VarName, TypeStr, node), ...]
|
||
indent: 缩进级别(用于嵌套类)
|
||
"""
|
||
lines: list[str] = []
|
||
PostdefVars = PostdefVars or []
|
||
IndentStr: str = ' ' * indent
|
||
|
||
# 处理类装饰器:virtual/novirtual 还原为关键字前缀
|
||
ClassPrefix: str = ''
|
||
for decorator in node.decorator_list:
|
||
DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator)
|
||
DecoratorShort: str = DecoratorStr.rsplit('.', 1)[-1]
|
||
if DecoratorShort == 'NoVTable':
|
||
ClassPrefix = 'novirtual '
|
||
elif DecoratorShort == 'CVTable':
|
||
ClassPrefix = 'virtual '
|
||
elif DecoratorStr:
|
||
lines.append(f'{IndentStr}@{DecoratorStr}')
|
||
|
||
# 检查是否是结构体、联合体或枚举
|
||
BaseNames: list[str] = [base.attr if isinstance(base, ast.Attribute) else base.id
|
||
for base in node.bases
|
||
if isinstance(base, (ast.Name, ast.Attribute))]
|
||
|
||
# 保留原始基类
|
||
if node.bases:
|
||
BaseStrs: list[str] = []
|
||
for base in node.bases:
|
||
if isinstance(base, ast.Name):
|
||
BaseStrs.append(base.id)
|
||
elif isinstance(base, ast.Attribute):
|
||
BaseStrs.append(f'{base.value.id}.{base.attr}')
|
||
elif isinstance(base, ast.Subscript):
|
||
# 处理泛型类型,如 memory_block_t[MAX_ORDER + 1]
|
||
BaseStrs.append(PythonToStubConverter._GetTypeString(base))
|
||
ClassTypeParamStr: str = ''
|
||
if hasattr(node, 'type_params') and node.type_params:
|
||
param_names: list[str] = [tp.name for tp in node.type_params]
|
||
ClassTypeParamStr = f'[{", ".join(param_names)}]'
|
||
if BaseStrs:
|
||
lines.append(f'{IndentStr}{ClassPrefix}class {node.name}{ClassTypeParamStr}({", ".join(BaseStrs)}):')
|
||
else:
|
||
lines.append(f'{IndentStr}{ClassPrefix}class {node.name}{ClassTypeParamStr}:')
|
||
else:
|
||
ClassTypeParamStr = ''
|
||
if hasattr(node, 'type_params') and node.type_params:
|
||
param_names = [tp.name for tp in node.type_params]
|
||
ClassTypeParamStr = f'[{", ".join(param_names)}]'
|
||
lines.append(f'{IndentStr}{ClassPrefix}class {node.name}{ClassTypeParamStr}:')
|
||
|
||
# 处理类成员
|
||
HasMembers: bool = False
|
||
seen_member_names: set[str] = set()
|
||
for item in node.body:
|
||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
seen_member_names.add(item.target.id)
|
||
init_members: list[ast.AnnAssign] = []
|
||
for item in node.body:
|
||
if isinstance(item, ast.FunctionDef) and item.name == '__init__':
|
||
for stmt in item.body:
|
||
if (isinstance(stmt, ast.AnnAssign)
|
||
and isinstance(stmt.target, ast.Attribute)
|
||
and isinstance(stmt.target.value, ast.Name)
|
||
and stmt.target.value.id == 'self'):
|
||
init_members.append(stmt)
|
||
untyped_self_members: list[ast.Assign] = []
|
||
for m_name in [m.target.attr for m in init_members if isinstance(m.target, ast.Attribute)]:
|
||
seen_member_names.add(m_name)
|
||
for item in node.body:
|
||
if isinstance(item, ast.FunctionDef):
|
||
for stmt in item.body:
|
||
if (isinstance(stmt, ast.Assign)
|
||
and len(stmt.targets) == 1
|
||
and isinstance(stmt.targets[0], ast.Attribute)
|
||
and isinstance(stmt.targets[0].value, ast.Name)
|
||
and stmt.targets[0].value.id == 'self'):
|
||
attr_name: str = stmt.targets[0].attr
|
||
if attr_name not in seen_member_names:
|
||
seen_member_names.add(attr_name)
|
||
untyped_self_members.append(stmt)
|
||
for item in list(node.body) + init_members + untyped_self_members:
|
||
if isinstance(item, ast.Assign):
|
||
if (len(item.targets) == 1
|
||
and isinstance(item.targets[0], ast.Attribute)
|
||
and isinstance(item.targets[0].value, ast.Name)
|
||
and item.targets[0].value.id == 'self'):
|
||
HasMembers = True
|
||
MemberIndent: str = IndentStr + ' '
|
||
attr_name = item.targets[0].attr
|
||
if attr_name in seen_member_names:
|
||
continue
|
||
InferredType: str = 'int'
|
||
if item.value and isinstance(item.value, ast.Constant):
|
||
if isinstance(item.value.value, float):
|
||
InferredType = 'float'
|
||
elif isinstance(item.value.value, bool):
|
||
InferredType = 'bool'
|
||
elif isinstance(item.value.value, str):
|
||
InferredType = 'str'
|
||
lines.append(f'{MemberIndent}{attr_name}: {InferredType}')
|
||
else:
|
||
HasMembers = True
|
||
MemberIndent = IndentStr + ' '
|
||
if SourceLines and hasattr(item, 'lineno'):
|
||
StartLine: int = item.lineno - 1
|
||
EndLine: int = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1
|
||
for i in range(StartLine, min(EndLine, len(SourceLines))):
|
||
lines.append(MemberIndent + SourceLines[i].lstrip())
|
||
elif isinstance(item, ast.AnnAssign):
|
||
HasMembers = True
|
||
TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation)
|
||
MemberIndent = IndentStr + ' '
|
||
# __provides__/__requires__/__require_must__ 元数据:保留值以便跨模块加载
|
||
if isinstance(item.target, ast.Name) and item.target.id in ('__provides__', '__requires__', '__require_must__'):
|
||
if item.value is not None:
|
||
try:
|
||
value_str: str = ast.unparse(item.value)
|
||
except Exception:
|
||
value_str = ''
|
||
lines.append(f'{MemberIndent}{item.target.id}: {TypeStr} = {value_str}')
|
||
else:
|
||
lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}')
|
||
continue
|
||
# 检查是否是宏变量(Markers 标记 define;CDefine 仅兼容旧文本)
|
||
_MemberMarkers: tuple[str, ...] = (
|
||
tuple(getattr(item, 'Markers', None) or ())
|
||
+ tuple(getattr(item.annotation, 'Markers', None) or ()))
|
||
if 'define' in _MemberMarkers or 'CDefine' in TypeStr:
|
||
# 检查宏变量是否应该被排除
|
||
if isinstance(item.target, ast.Name):
|
||
MacroName: str = item.target.id
|
||
if PythonToStubConverter._ShouldExcludeMacro(MacroName):
|
||
continue # 跳过该宏
|
||
# 统一输出新语法 `define NAME[: 类型] = 值`
|
||
ImplicitType: bool = (
|
||
isinstance(item.annotation, ast.Name)
|
||
and item.annotation.id == 'CDefine')
|
||
TypePart: str = '' if ImplicitType else f': {TypeStr}'
|
||
if item.value is not None:
|
||
try:
|
||
ValueStr: str = ast.unparse(item.value)
|
||
except Exception:
|
||
ValueStr = ''
|
||
lines.append(f'{MemberIndent}define {item.target.id}{TypePart} = {ValueStr}')
|
||
else:
|
||
lines.append(f'{MemberIndent}define {item.target.id}{TypePart} = 0')
|
||
else:
|
||
if isinstance(item.target, ast.Attribute):
|
||
MemberName: str = item.target.attr
|
||
else:
|
||
MemberName = item.target.id
|
||
FinalTypeStr: str = TypeStr
|
||
if IsListAnnotation(item.annotation):
|
||
slice_node: ast.AST = item.annotation.slice
|
||
has_size: bool = False
|
||
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
|
||
count_node: ast.AST = slice_node.elts[1]
|
||
if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int) and count_node.value > 0:
|
||
has_size = True
|
||
else:
|
||
try:
|
||
count_expr: str = ast.unparse(count_node)
|
||
except Exception:
|
||
count_expr = ''
|
||
if count_expr and count_expr != 'None':
|
||
has_size = True
|
||
elem_type_str: str = PythonToStubConverter._GetTypeString(slice_node.elts[0])
|
||
FinalTypeStr = f'CArray[{elem_type_str}, {count_expr}]'
|
||
if not has_size:
|
||
elem_type_str = PythonToStubConverter._GetTypeString(slice_node if not isinstance(slice_node, ast.Tuple) else slice_node.elts[0])
|
||
init_len: int = 0
|
||
if item.value is not None:
|
||
if isinstance(item.value, ast.List):
|
||
init_len = len(item.value.elts)
|
||
elif isinstance(item.value, ast.Constant) and isinstance(item.value.value, str):
|
||
init_len = len(item.value.value) + 1
|
||
if init_len > 0:
|
||
FinalTypeStr = f'CArray[{elem_type_str}, {init_len}]'
|
||
else:
|
||
FinalTypeStr = f'CArray[{elem_type_str}, None]'
|
||
if item.value is not None and isinstance(item.value, ast.Constant):
|
||
try:
|
||
value_repr: str = ast.unparse(item.value)
|
||
lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr} = {value_repr}')
|
||
except Exception:
|
||
lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}')
|
||
else:
|
||
lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}')
|
||
elif isinstance(item, ast.FunctionDef):
|
||
HasMembers = True
|
||
FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent+1, SourceLines=SourceLines, class_name=node.name)
|
||
lines.extend(FuncStub)
|
||
elif isinstance(item, ast.Expr):
|
||
# 处理宏调用,如 CUndef(...) / CPragma(...)
|
||
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
|
||
if ExprStr:
|
||
lines.append(f'{IndentStr} {ExprStr}')
|
||
elif isinstance(item, ast.If):
|
||
# 处理 cif 宏条件(`cif ...:` / `celif ...:` / `celse:`)
|
||
IfStr: str = PythonToStubConverter._get_if_macro_string(item, indent=indent+1)
|
||
if IfStr:
|
||
lines.append(IfStr)
|
||
elif isinstance(item, ast.ClassDef):
|
||
# 处理嵌套类
|
||
HasMembers = True
|
||
NestedClassLines: list[str] = PythonToStubConverter._ConvertClass(item, SourceLines=SourceLines, PostdefVars=[], indent=indent+1)
|
||
# 移除嵌套类的宏守卫(因为已经在父类的宏守卫内)
|
||
FilteredLines: list[str] = []
|
||
for line in NestedClassLines:
|
||
# 跳过宏守卫相关行
|
||
if line.strip().startswith('CIfndef(') or line.strip().startswith('CEndif()'):
|
||
continue
|
||
if ': CDefine' in line and '__' in line:
|
||
continue
|
||
FilteredLines.append(line)
|
||
lines.extend(FilteredLines)
|
||
|
||
if not HasMembers:
|
||
lines.append(f'{IndentStr} pass')
|
||
|
||
# 添加 Postdefinition 变量
|
||
if PostdefVars:
|
||
lines.append('')
|
||
for VarName, TypeStr, _ in PostdefVars:
|
||
lines.append(f'{VarName}: {TypeStr}')
|
||
|
||
return lines
|
||
|
||
@staticmethod
|
||
def _ConvertFunction(node: ast.FunctionDef, indent: int = 0, SourceLines: list[str] | None = None, class_name: str | None = None) -> list[str]:
|
||
"""转换函数定义"""
|
||
lines: list[str] = []
|
||
IndentStr: str = ' ' * indent
|
||
|
||
# 检查是否是宏函数:新语法由解析器写 node.Define;旧 `-> CDefine`
|
||
# 返回注解仅作兼容回落。
|
||
ReturnMarkers: tuple[str, ...] = getattr(node.returns, 'Markers', None) or () if node.returns else ()
|
||
is_macro_func: bool = bool(getattr(node, 'Define', False)) or 'define' in ReturnMarkers
|
||
if node.returns:
|
||
ReturnType: str = PythonToStubConverter._GetTypeString(node.returns)
|
||
if 'CDefine' in ReturnType:
|
||
is_macro_func = True
|
||
|
||
# 如果是宏函数,检查是否应该被排除
|
||
if is_macro_func:
|
||
if PythonToStubConverter._ShouldExcludeMacro(node.name):
|
||
return lines # 返回空列表,排除该宏
|
||
|
||
# 保持原样(包括代码块)
|
||
if SourceLines:
|
||
StartLine: int = node.lineno - 1
|
||
EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1
|
||
Copied: list[str] = [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(':'):
|
||
pass
|
||
elif lines:
|
||
lines.append(f'{IndentStr} pass')
|
||
else:
|
||
# 源区间为空(理论不可达):产出合法占位,绝不落出 ` pass` 这类非法缩进
|
||
lines.append(f'{IndentStr}pass')
|
||
return lines
|
||
|
||
# 处理函数装饰器:存储类/一次性初始化还原为关键字前缀。
|
||
# 新语法的 `extern def` / `export def` / `static def` 由解析器写入
|
||
# FunctionDef.Extern/Export/Static;旧装饰器形态仅作兼容。
|
||
IsExternFunc: bool = bool(getattr(node, 'Extern', False))
|
||
IsExportFunc: bool = bool(getattr(node, 'Export', False))
|
||
IsStaticFunc: bool = bool(getattr(node, 'Static', False))
|
||
IsTlsFunc: bool = False
|
||
IsVirtualFunc: bool = False
|
||
IsNoVirtualFunc: bool = False
|
||
PendingDecorators: list[str] = []
|
||
for decorator in node.decorator_list:
|
||
DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator)
|
||
DecoratorShort: str = DecoratorStr.rsplit('.', 1)[-1]
|
||
if DecoratorShort == 'TLS':
|
||
IsTlsFunc = True
|
||
elif DecoratorShort == 'CStatic':
|
||
IsStaticFunc = True
|
||
elif DecoratorShort == 'CExport':
|
||
IsExportFunc = True
|
||
elif DecoratorShort == 'CExtern':
|
||
IsExternFunc = True
|
||
elif DecoratorShort == 'CVTable':
|
||
IsVirtualFunc = True
|
||
elif DecoratorShort == 'NoVTable':
|
||
IsNoVirtualFunc = True
|
||
elif DecoratorStr:
|
||
PendingDecorators.append(DecoratorStr)
|
||
FuncPrefix: str = ''
|
||
if IsExternFunc:
|
||
FuncPrefix = 'extern '
|
||
elif IsExportFunc:
|
||
FuncPrefix = 'export '
|
||
elif IsStaticFunc:
|
||
FuncPrefix = 'static '
|
||
elif IsTlsFunc:
|
||
FuncPrefix = 'tls '
|
||
elif IsVirtualFunc:
|
||
FuncPrefix = 'virtual '
|
||
elif IsNoVirtualFunc:
|
||
FuncPrefix = 'novirtual '
|
||
for DecoratorStr in PendingDecorators:
|
||
lines.append(f'{IndentStr}@{DecoratorStr}')
|
||
|
||
# 构建参数列表
|
||
# 治本修复:原实现只遍历 node.args.args,完全忽略 node.args.defaults,
|
||
# 导致带默认值的参数(如 category: str = "")在 .pyi 中丢失默认值。
|
||
# 下游 DeclarationGenerator 据此生成 stub.ll 时,会因缺少默认参数而
|
||
# 生成参数数量不足的 declare,进而造成调用方 ABI 不匹配崩溃。
|
||
# 修复:按 Python AST 语义,defaults 从右向左对应 args 末尾的参数,
|
||
# 对有默认值的参数附加 '= <default_value>'。
|
||
params: list[str] = []
|
||
NumDefaults: int = len(node.args.defaults) if node.args.defaults else 0
|
||
DefaultsStart: int = len(node.args.args) - NumDefaults
|
||
for arg_idx, arg in enumerate(node.args.args):
|
||
ArgName: str = arg.arg
|
||
if arg.annotation:
|
||
TypeStr: str = PythonToStubConverter._GetTypeString(arg.annotation)
|
||
ParamPart: str = f'{ArgName}: {TypeStr}'
|
||
elif class_name and arg_idx == 0 and ArgName == 'self':
|
||
ParamPart = f'self: {class_name}'
|
||
else:
|
||
ParamPart = ArgName
|
||
# 附加默认值:defaults[i] 对应 args[DefaultsStart + i]
|
||
if NumDefaults > 0 and arg_idx >= DefaultsStart:
|
||
DefaultIdx: int = arg_idx - DefaultsStart
|
||
try:
|
||
DefaultStr: str = ast.unparse(node.args.defaults[DefaultIdx])
|
||
ParamPart = f'{ParamPart} = {DefaultStr}'
|
||
except Exception:
|
||
# unparse 失败时保留无默认值形式,避免阻塞生成
|
||
pass
|
||
params.append(ParamPart)
|
||
|
||
# 处理 *args
|
||
if node.args.vararg:
|
||
ArgName = node.args.vararg.arg
|
||
if node.args.vararg.annotation:
|
||
TypeStr = PythonToStubConverter._GetTypeString(node.args.vararg.annotation)
|
||
params.append(f'*{ArgName}: {TypeStr}')
|
||
else:
|
||
params.append(f'*{ArgName}')
|
||
|
||
# 处理 **kwargs
|
||
if node.args.kwarg:
|
||
ArgName = node.args.kwarg.arg
|
||
if node.args.kwarg.annotation:
|
||
TypeStr = PythonToStubConverter._GetTypeString(node.args.kwarg.annotation)
|
||
params.append(f'**{ArgName}: {TypeStr}')
|
||
else:
|
||
params.append(f'**{ArgName}')
|
||
|
||
ParamStr: str = ', '.join(params)
|
||
|
||
# 返回类型 - 保持原始类型(不添加 State)
|
||
if node.returns:
|
||
ReturnType = PythonToStubConverter._GetTypeString(node.returns)
|
||
else:
|
||
ReturnType = 'int'
|
||
|
||
TypeParamStr: str = ''
|
||
if hasattr(node, 'type_params') and node.type_params:
|
||
param_names: list[str] = [tp.name for tp in node.type_params]
|
||
TypeParamStr = f'[{", ".join(param_names)}]'
|
||
|
||
lines.append(f'{IndentStr}{FuncPrefix}def {node.name}{TypeParamStr}({ParamStr}) -> {ReturnType}: pass')
|
||
return lines
|
||
|
||
@staticmethod
|
||
def _ConvertVariable(node: ast.AnnAssign, SourceLines: list[str] | None = None) -> list[str]:
|
||
"""转换变量定义"""
|
||
lines: list[str] = []
|
||
if isinstance(node.target, ast.Name):
|
||
VarName: str = node.target.id
|
||
TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation)
|
||
Markers: tuple[str, ...] = (tuple(getattr(node, 'Markers', None) or ())
|
||
+ tuple(getattr(node.annotation, 'Markers', None) or ()))
|
||
# 检查是否是宏变量(优先读 Markers 的 define;CDefine 仅兼容旧文本)
|
||
if 'CDefine' in TypeStr or 'define' in Markers:
|
||
# 检查宏变量是否应该被排除
|
||
if PythonToStubConverter._ShouldExcludeMacro(VarName):
|
||
return lines # 返回空列表,排除该宏
|
||
# 统一输出新语法 `define NAME[: 类型] = 值`(旧写法在此归一化)
|
||
ImplicitType: bool = (
|
||
isinstance(node.annotation, ast.Name)
|
||
and node.annotation.id == 'CDefine')
|
||
TypePart: str = '' if ImplicitType else f': {TypeStr}'
|
||
if node.value is not None:
|
||
try:
|
||
ValueStr: str = ast.unparse(node.value)
|
||
except Exception:
|
||
ValueStr = ''
|
||
lines.append(f'define {VarName}{TypePart} = {ValueStr}')
|
||
else:
|
||
# 无初值的宏(C 头 `#define FOO`)在新语法下补 0 占位
|
||
lines.append(f'define {VarName}{TypePart} = 0')
|
||
return lines
|
||
# 非宏变量:不加 State
|
||
# 含有 typedef 的变量不加 extern,普通变量需要加 extern
|
||
has_typedef: bool = 'CTypedef' in TypeStr or 'typedef' in Markers
|
||
if has_typedef:
|
||
ValueNode: ast.AST | None = node.value if node.value is not None else node.annotation
|
||
ValueTypeStr: str = PythonToStubConverter._GetTypeString(ValueNode)
|
||
lines.append(f'typedef {VarName} = {ValueTypeStr}')
|
||
elif 'extern' in Markers:
|
||
lines.append(f'extern {VarName}: {TypeStr}')
|
||
elif 'static' in Markers:
|
||
lines.append(f'static {VarName}: {TypeStr}')
|
||
elif 'export' in Markers:
|
||
lines.append(f'export {VarName}: {TypeStr}')
|
||
else:
|
||
lines.append(f'{VarName}: {TypeStr}')
|
||
return lines
|
||
|
||
@staticmethod
|
||
def _ConvertGlobalAssign(node: ast.Assign) -> list[str]:
|
||
"""转换模块级别的全局变量(无类型标注)"""
|
||
lines: list[str] = []
|
||
if not node.targets or not isinstance(node.targets[0], ast.Name):
|
||
return lines
|
||
|
||
VarName: str = node.targets[0].id
|
||
|
||
# 跳过私有变量(以下划线开头)
|
||
if VarName.startswith('_'):
|
||
return lines
|
||
|
||
# 从值推断类型
|
||
inferred_type: str | None = PythonToStubConverter._InferTypeFromValue(node.value)
|
||
if inferred_type:
|
||
lines.append(f'{VarName}: {inferred_type}')
|
||
else:
|
||
lines.append(f'{VarName}: int')
|
||
|
||
return lines
|
||
|
||
@staticmethod
|
||
def _InferTypeFromValue(value: ast.AST) -> str | None:
|
||
"""从值推断内建类型名(新语法:int/float/char*/rawptr)"""
|
||
if isinstance(value, ast.Constant):
|
||
if isinstance(value.value, bool):
|
||
return 'int'
|
||
elif isinstance(value.value, int):
|
||
return 'int'
|
||
elif isinstance(value.value, float):
|
||
return 'double'
|
||
elif isinstance(value.value, str):
|
||
return 'char*'
|
||
elif isinstance(value, ast.List):
|
||
return 'rawptr'
|
||
elif isinstance(value, ast.Dict):
|
||
return 'rawptr'
|
||
elif isinstance(value, ast.Name):
|
||
return 'int'
|
||
elif isinstance(value, ast.BinOp):
|
||
left_type: str | None = PythonToStubConverter._InferTypeFromValue(value.left)
|
||
if left_type:
|
||
return left_type
|
||
return 'int'
|
||
elif isinstance(value, ast.Call):
|
||
# 函数调用返回值,假设为指针类型
|
||
if isinstance(value.func, ast.Name):
|
||
func_name: str = value.func.id
|
||
if func_name in ('malloc', 'cast'):
|
||
return 'rawptr'
|
||
return 'rawptr'
|
||
return None
|
||
|
||
@staticmethod
|
||
def _GetTypeString(annotation: ast.AST) -> str:
|
||
if annotation is None:
|
||
return 'void'
|
||
Core: str = PythonToStubConverter._GetTypeStringCore(annotation)
|
||
# 语句级修饰(typedef/define/static/export/extern/state)由调用方在
|
||
# 声明前缀输出,不能混进类型文本;const/volatile/inline 等类型修饰
|
||
# 以及 Attributes/ByteOrder/BitWidth 按 Unparser 同一口径还原。
|
||
_StatementMarkers: frozenset[str] = frozenset(
|
||
('typedef', 'define', 'static', 'export', 'extern', 'state'))
|
||
Markers: tuple[str, ...] = tuple(getattr(annotation, 'Markers', None) or ())
|
||
PrefixParts: list[str] = [
|
||
m for m in Markers if m not in _StatementMarkers]
|
||
Attributes: tuple[str, ...] = tuple(getattr(annotation, 'Attributes', None) or ())
|
||
if Attributes:
|
||
PrefixParts.append('__attribute__((' + ', '.join(Attributes) + '))')
|
||
ByteOrder: str = getattr(annotation, 'ByteOrder', '') or ''
|
||
if ByteOrder == 'big':
|
||
PrefixParts.append('BigEndian')
|
||
elif ByteOrder == 'little':
|
||
PrefixParts.append('LittleEndian')
|
||
BitWidth: int = getattr(annotation, 'BitWidth', 0) or 0
|
||
if BitWidth:
|
||
PrefixParts.append(f'Bit({BitWidth})')
|
||
Prefix: str = ''.join(f'{m} ' for m in PrefixParts)
|
||
return Prefix + Core
|
||
|
||
@staticmethod
|
||
def _GetTypeStringCore(annotation: ast.AST) -> str:
|
||
"""获取类型字符串
|
||
|
||
支持集与 VPC(ViperC/App/lib/Stub/StubGen/Converter.vp 的 _PyiTypeStr)
|
||
等价:Name/Attribute/Subscript/BinOp/UnaryOp/Compare/BoolOp/Constant/
|
||
List/Call(VPC 另支持裸 Tuple,本侧仅在 Subscript.slice 位置接受 Tuple)。
|
||
未支持的节点种类与运算符在此 fail-fast(不自造 'CVoid' 回落)。
|
||
"""
|
||
# 无注解(None):调用方未提供注解,按无返回类型处理(新语法 void);
|
||
# 这不是"未知节点"——未知节点走函数尾 fail-fast。
|
||
if annotation is None:
|
||
return 'void'
|
||
if isinstance(annotation, ast.Name):
|
||
return _STUB_TYPE_ALIASES.get(annotation.id, annotation.id)
|
||
elif isinstance(annotation, ast.Attribute):
|
||
if isinstance(annotation.value, ast.Name):
|
||
if annotation.value.id in ('t', 'c'):
|
||
return _STUB_TYPE_ALIASES.get(annotation.attr, annotation.attr)
|
||
return f'{annotation.value.id}.{annotation.attr}'
|
||
else:
|
||
# 处理嵌套属性访问,如 attr.packed
|
||
ParentStr: str = PythonToStubConverter._GetTypeString(annotation.value)
|
||
return f'{ParentStr}.{annotation.attr}'
|
||
elif isinstance(annotation, ast.Subscript):
|
||
# 值引用标记按节点原名判定(不能被 _STUB_TYPE_ALIASES 提前归一成
|
||
# rawptr,否则 `&T` / `&temp T` 会被误写成 `T*`)。
|
||
ValueName: str = ''
|
||
if isinstance(annotation.value, ast.Name):
|
||
ValueName = annotation.value.id
|
||
elif isinstance(annotation.value, ast.Attribute):
|
||
ValueName = annotation.value.attr
|
||
if ValueName == 'CRef':
|
||
return '&' + PythonToStubConverter._GetTypeString(annotation.slice)
|
||
if ValueName == 'CTempRef':
|
||
return '&temp ' + PythonToStubConverter._GetTypeString(annotation.slice)
|
||
base: str = PythonToStubConverter._GetTypeString(annotation.value)
|
||
if isinstance(annotation.slice, ast.Tuple):
|
||
slice_strs: list[str] = [PythonToStubConverter._GetTypeString(e) for e in annotation.slice.elts]
|
||
SliceStr: str = ', '.join(slice_strs)
|
||
else:
|
||
SliceStr = PythonToStubConverter._GetTypeString(annotation.slice)
|
||
if base in ('CPtr', 'rawptr'):
|
||
return PythonToStubConverter._GetTypeString(annotation.slice) + '*'
|
||
return f'{base}[{SliceStr}]'
|
||
elif isinstance(annotation, ast.BinOp):
|
||
# 类型注解不再接受 `T1 | T2` 联合(包括旧的 CExtern | T /
|
||
# CDefine | T / T | CRef):一律 fail-fast,不做任何合并。
|
||
if isinstance(annotation.op, ast.BitOr):
|
||
Line: int = getattr(annotation, 'lineno', 0) or 0
|
||
raise TypeError(
|
||
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
|
||
f"类型注解只能是单一类型:指针请写 `T*` 或 `rawptr[T]`,"
|
||
f"修饰符请用关键字(const/volatile/static/export/extern)")
|
||
LeftStr: str = PythonToStubConverter._GetTypeString(annotation.left)
|
||
RightStr: str = PythonToStubConverter._GetTypeString(annotation.right)
|
||
if isinstance(annotation.op, ast.Add):
|
||
return f'{LeftStr} + {RightStr}'
|
||
elif isinstance(annotation.op, ast.Sub):
|
||
return f'{LeftStr} - {RightStr}'
|
||
elif isinstance(annotation.op, ast.Mult):
|
||
return f'{LeftStr} * {RightStr}'
|
||
elif isinstance(annotation.op, ast.Div):
|
||
return f'{LeftStr} / {RightStr}'
|
||
elif isinstance(annotation.op, ast.Mod):
|
||
return f'{LeftStr} % {RightStr}'
|
||
# 位与/右移:条件编译实参(如 cif MASK_BYTE & 0x0F、
|
||
# cif (MAX_SIZE >> SHIFT_BITS) > 0)。两运算符必须与 VPC 的
|
||
# _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(与 VPC 同语义)
|
||
elif isinstance(annotation, ast.UnaryOp):
|
||
if isinstance(annotation.op, ast.Not):
|
||
operand: str = PythonToStubConverter._GetTypeString(annotation.operand)
|
||
return f'not {operand}'
|
||
elif isinstance(annotation.op, ast.Invert):
|
||
operand = PythonToStubConverter._GetTypeString(annotation.operand)
|
||
return f'~{operand}'
|
||
elif isinstance(annotation.op, ast.USub):
|
||
operand = PythonToStubConverter._GetTypeString(annotation.operand)
|
||
return f'-{operand}'
|
||
elif isinstance(annotation, ast.Compare):
|
||
# 处理比较操作,如 FF_MAX_SS != FF_MIN_SS
|
||
left: str = PythonToStubConverter._GetTypeString(annotation.left)
|
||
comparators: list[str] = [PythonToStubConverter._GetTypeString(c) for c in annotation.comparators]
|
||
ops: list[str] = []
|
||
for op in annotation.ops:
|
||
if isinstance(op, ast.Eq):
|
||
ops.append('==')
|
||
elif isinstance(op, ast.NotEq):
|
||
ops.append('!=')
|
||
elif isinstance(op, ast.Lt):
|
||
ops.append('<')
|
||
elif isinstance(op, ast.LtE):
|
||
ops.append('<=')
|
||
elif isinstance(op, ast.Gt):
|
||
ops.append('>')
|
||
elif isinstance(op, ast.GtE):
|
||
ops.append('>=')
|
||
else:
|
||
ops.append('?')
|
||
result: str = left
|
||
for i, op in enumerate(ops):
|
||
result += f' {op} {comparators[i]}'
|
||
return result
|
||
elif isinstance(annotation, ast.BoolOp):
|
||
# 布尔运算:条件编译实参(如 cif ENABLED and MAX_SIZE > 0)。
|
||
# 文本与 VPC 的 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):
|
||
return repr(annotation.value)
|
||
elif isinstance(annotation, ast.Index):
|
||
return PythonToStubConverter._GetTypeString(annotation.value)
|
||
elif isinstance(annotation, ast.List):
|
||
# 处理列表,如 [MAX_FILE_DESCRIPTORS]
|
||
elements: list[str] = [PythonToStubConverter._GetTypeString(elem) for elem in annotation.elts]
|
||
return f'[{", ".join(elements)}]'
|
||
elif isinstance(annotation, ast.Call):
|
||
# 处理函数调用形式的类型注解,如 Postdefinition(xxa) 或 callable(CVoid, app_id = CInt)
|
||
# 如果是 __set_default__ 调用,只返回函数名部分
|
||
if isinstance(annotation.func, ast.Attribute) and annotation.func.attr in ('__set_default__', '__set_default'):
|
||
return PythonToStubConverter._GetTypeString(annotation.func.value)
|
||
|
||
FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func)
|
||
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in annotation.args])
|
||
# 处理关键字参数
|
||
keywords_str: str = ', '.join([f'{kw.arg} = {PythonToStubConverter._GetTypeString(kw.value)}' for kw in annotation.keywords])
|
||
# 合并位置参数和关键字参数
|
||
if ArgsStr and keywords_str:
|
||
return f'{FuncStr}({ArgsStr}, {keywords_str})'
|
||
elif keywords_str:
|
||
return f'{FuncStr}({keywords_str})'
|
||
else:
|
||
return f'{FuncStr}({ArgsStr})'
|
||
# 未知/未支持的注解节点 fail-fast(与 VPC StubGen 的 _PyiTypeStr 同语义、
|
||
# 同文案、同类型):静默回落 'CVoid' 会把错误类型写进 stub 签名——实例:
|
||
# 源里的 Callable[[], int] 曾写成 Callable[int, int](见
|
||
# ViperApps/ViperOS/VKernel/__vpcbuild__/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
|
||
def _ExtractPostdefClass(annotation: ast.AST) -> str | None:
|
||
"""从 Postdefinition 类型注解中提取类名
|
||
|
||
例如:Postdefinition(__buddy_system) -> '__buddy_system'
|
||
Postdefinition(xxx) | CTypedef -> 'xxx'
|
||
"""
|
||
# 类型注解中的 BinOp(BitOr) 联合已废止:fail-fast,不做合并。
|
||
if isinstance(annotation, ast.BinOp):
|
||
if isinstance(annotation.op, ast.BitOr):
|
||
Line: int = getattr(annotation, 'lineno', 0) or 0
|
||
raise TypeError(
|
||
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
|
||
f"类型注解只能是单一类型")
|
||
return None
|
||
|
||
# 处理函数调用 (如: Postdefinition(__buddy_system))
|
||
if isinstance(annotation, ast.Call):
|
||
FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func)
|
||
if FuncStr == 'Postdefinition' and annotation.args:
|
||
# 提取第一个参数
|
||
first_arg: ast.AST = annotation.args[0]
|
||
if isinstance(first_arg, ast.Name):
|
||
return first_arg.id
|
||
elif isinstance(first_arg, ast.Attribute):
|
||
return f'{first_arg.value.id}.{first_arg.attr}'
|
||
|
||
return None
|
||
|
||
@staticmethod
|
||
def _GetImportString(node: ast.Import, SourceLines: list[str] | None = None) -> str:
|
||
"""获取导入语句字符串"""
|
||
parts: list[str] = []
|
||
IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap
|
||
|
||
for alias in node.names:
|
||
ModuleName: str = alias.name
|
||
# 检查是否是 __include.xxx 格式的导入
|
||
if ModuleName.startswith('__include.'):
|
||
# 提取别名(xxx 部分)
|
||
SubModule: str = ModuleName[len('__include.'):]
|
||
if SubModule in IncludeAliasMap:
|
||
# 替换为实际模块路径
|
||
RealModule: str = IncludeAliasMap[SubModule]
|
||
if alias.asname:
|
||
parts.append(f'{RealModule} as {alias.asname}')
|
||
else:
|
||
parts.append(f'{RealModule} as {SubModule}')
|
||
else:
|
||
# 不在映射表中,保持原样
|
||
if alias.asname:
|
||
parts.append(f'{ModuleName} as {alias.asname}')
|
||
else:
|
||
parts.append(ModuleName)
|
||
else:
|
||
if alias.asname:
|
||
parts.append(f'{alias.name} as {alias.asname}')
|
||
else:
|
||
parts.append(alias.name)
|
||
|
||
result: str = f'import {", ".join(parts)}'
|
||
|
||
# 提取并保留注释
|
||
if SourceLines and hasattr(node, 'lineno'):
|
||
LineIdx: int = node.lineno - 1
|
||
if 0 <= LineIdx < len(SourceLines):
|
||
line: str = SourceLines[LineIdx]
|
||
CommentMatch: re.Match | None = _RE_LINE_COMMENT.search(line)
|
||
if CommentMatch:
|
||
result += ' ' + CommentMatch.group(0)
|
||
|
||
return result
|
||
|
||
@staticmethod
|
||
def _GetImportFromString(node: ast.ImportFrom, SourceLines: list[str] | None = None) -> str:
|
||
"""获取 from ... import ... 语句字符串"""
|
||
module: str = node.module or ''
|
||
parts: list[str] = []
|
||
IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap
|
||
|
||
# 处理相对导入 (from . import xxx 或 from ..package import xxx)
|
||
if node.level > 0:
|
||
if module:
|
||
if node.level == 1:
|
||
full_module: str = f'.{module}'
|
||
else:
|
||
full_module = '.' * node.level + module
|
||
else:
|
||
full_module = '.' * node.level
|
||
for alias in node.names:
|
||
if alias.asname:
|
||
parts.append(f'{alias.name} as {alias.asname}')
|
||
else:
|
||
parts.append(alias.name)
|
||
return f'from {full_module} import {", ".join(parts)}'
|
||
|
||
# 提取注释(在生成结果前获取)
|
||
comment: str = ''
|
||
if SourceLines and hasattr(node, 'lineno'):
|
||
LineIdx = node.lineno - 1
|
||
if 0 <= LineIdx < len(SourceLines):
|
||
line = SourceLines[LineIdx]
|
||
CommentMatch: re.Match | None = _RE_LINE_COMMENT.search(line)
|
||
if CommentMatch:
|
||
comment = ' ' + CommentMatch.group(0)
|
||
|
||
# 检查是否是 from __include import xxx 格式
|
||
if module == '__include':
|
||
for alias in node.names:
|
||
name: str = alias.name
|
||
if name in IncludeAliasMap:
|
||
# 替换为实际模块路径
|
||
RealModule: str = IncludeAliasMap[name]
|
||
if alias.asname:
|
||
parts.append(f'import {RealModule} as {alias.asname}{comment}')
|
||
else:
|
||
parts.append(f'import {RealModule} as {name}{comment}')
|
||
else:
|
||
# 不在映射表中,保持原样
|
||
if alias.asname:
|
||
parts.append(f'from {module} import {name} as {alias.asname}{comment}')
|
||
else:
|
||
parts.append(f'from {module} import {name}{comment}')
|
||
return '\n'.join(parts)
|
||
|
||
# 普通 from ... import ... 语句
|
||
for alias in node.names:
|
||
if alias.asname:
|
||
parts.append(f'{alias.name} as {alias.asname}')
|
||
else:
|
||
parts.append(alias.name)
|
||
return f'from {module} import {", ".join(parts)}{comment}'
|
||
|
||
@staticmethod
|
||
def _GetDecoratorString(decorator: ast.AST) -> str:
|
||
"""获取装饰器字符串"""
|
||
if isinstance(decorator, ast.Name):
|
||
return decorator.id
|
||
elif isinstance(decorator, ast.Attribute):
|
||
if isinstance(decorator.value, ast.Name):
|
||
# 旧 `t.X` / `c.X` 前缀只作输入兼容:stub 输出按裸名书写。
|
||
if decorator.value.id in ('t', 'c'):
|
||
return decorator.attr
|
||
return f'{decorator.value.id}.{decorator.attr}'
|
||
return decorator.attr
|
||
elif isinstance(decorator, ast.Call):
|
||
# 处理带参数的装饰器,如 @Attribute(attr.packed)
|
||
FuncStr: str = PythonToStubConverter._GetDecoratorString(decorator.func)
|
||
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in decorator.args])
|
||
return f'{FuncStr}({ArgsStr})'
|
||
return ''
|
||
|
||
@staticmethod
|
||
def _GetExprString(expr: ast.AST) -> str:
|
||
"""获取表达式字符串(用于宏调用)"""
|
||
if isinstance(expr, ast.Call):
|
||
# 处理函数调用,如 CUndef(...), CPragma(...)
|
||
# (旧的 CIf/CElif/CElse 已删除:cif/celif/celse 由 _ProcessIfMacro 渲染)
|
||
FuncStr: str = PythonToStubConverter._GetDecoratorString(expr.func)
|
||
if FuncStr and ('CEndif' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr):
|
||
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in expr.args])
|
||
return f'{FuncStr}({ArgsStr})' if ArgsStr else f'{FuncStr}()'
|
||
return ''
|
||
|
||
@staticmethod
|
||
def _RestoreDefinedMarkers(node: ast.AST) -> ast.AST:
|
||
"""把内部标记 @CIfdef(N) 递归还原为 defined(N) 调用(供 stub 文本渲染)。
|
||
|
||
cif/celif 条件的 desugar 会把 defined(NAME) 变成 @CIfdef(NAME) 标记调用;
|
||
生成 .vpi 文本时必须还原为用户可写的 defined(NAME),否则存根不可再解析。
|
||
"""
|
||
if isinstance(node, ast.Call):
|
||
F: str = PythonToStubConverter._GetDecoratorString(node.func)
|
||
short: str = F.rsplit('.', 1)[-1] if F else ''
|
||
if short in ('@CIfdef', '@CIfndef') and len(node.args) == 1:
|
||
inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(node.args[0])
|
||
call: ast.Call = ast.Call(
|
||
func=ast.Name(id='defined', ctx=ast.Load()),
|
||
args=[inner], keywords=[])
|
||
if short == '@CIfndef':
|
||
return ast.UnaryOp(op=ast.Not(), operand=call)
|
||
return call
|
||
for field, value in ast.iter_fields(node):
|
||
if isinstance(value, list):
|
||
for i, item in enumerate(value):
|
||
if isinstance(item, ast.AST):
|
||
value[i] = PythonToStubConverter._RestoreDefinedMarkers(item)
|
||
elif isinstance(value, ast.AST):
|
||
setattr(node, field, PythonToStubConverter._RestoreDefinedMarkers(value))
|
||
return node
|
||
|
||
@staticmethod
|
||
def _CifConditionText(test: ast.AST) -> str | None:
|
||
"""cif/celif 条件文本:内部标记 @CIf(EXPR) → EXPR;@CIfdef(N) → defined(N)。
|
||
|
||
返回 None 表示 test 不是 cif 族标记(普通 if 的运行时条件)。
|
||
"""
|
||
if isinstance(test, ast.Call):
|
||
F: str = PythonToStubConverter._GetDecoratorString(test.func)
|
||
short: str = F.rsplit('.', 1)[-1] if F else ''
|
||
if short == '@CIf' and len(test.args) == 1:
|
||
inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(test.args[0])
|
||
return PythonToStubConverter._GetTypeString(inner)
|
||
if short in ('@CIfdef', '@CIfndef') and len(test.args) == 1:
|
||
return PythonToStubConverter._GetTypeString(
|
||
PythonToStubConverter._RestoreDefinedMarkers(test))
|
||
return None
|
||
|
||
@staticmethod
|
||
def _get_if_macro_string(node: ast.If, indent: int = 1) -> str:
|
||
"""获取类内部的 cif 宏字符串(如 `cif <cond>:`,含 celif/celse 链)。"""
|
||
IndentStr: str = ' ' * indent
|
||
InnerIndent: str = ' ' * (indent + 1)
|
||
cond: str | None = PythonToStubConverter._CifConditionText(node.test)
|
||
if cond is None:
|
||
return ''
|
||
|
||
def _body_lines(body: list, ind: str) -> list[str]:
|
||
out: list[str] = []
|
||
for item in body:
|
||
if isinstance(item, ast.AnnAssign):
|
||
TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation)
|
||
out.append(f'{ind}{item.target.id}: {TypeStr}')
|
||
elif isinstance(item, ast.Expr):
|
||
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
|
||
if ExprStr:
|
||
out.append(f'{ind}{ExprStr}')
|
||
return out
|
||
|
||
result: list[str] = [f'{IndentStr}cif {cond}:']
|
||
result.extend(_body_lines(node.body, InnerIndent))
|
||
current: ast.If = node
|
||
while current.orelse:
|
||
if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If):
|
||
elif_node: ast.If = current.orelse[0]
|
||
econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test)
|
||
if econd is None:
|
||
break
|
||
result.append(f'{IndentStr}celif {econd}:')
|
||
result.extend(_body_lines(elif_node.body, InnerIndent))
|
||
current = elif_node
|
||
continue
|
||
result.append(f'{IndentStr}celse:')
|
||
result.extend(_body_lines(current.orelse, InnerIndent))
|
||
break
|
||
return '\n'.join(result)
|
||
|
||
@staticmethod
|
||
def _GetModuleIfMacroString(node: ast.If) -> str:
|
||
"""获取模块级别的 cif 宏字符串(如 `cif FF_MULTI_PARTITION:`)。"""
|
||
return PythonToStubConverter._ProcessIfMacro(node, indent=0)
|
||
|
||
@staticmethod
|
||
def _ProcessIfMacro(node: ast.If, indent: int = 0) -> str:
|
||
"""处理 cif 宏节点,支持 celif / celse 链。"""
|
||
IndentStr: str = ' ' * indent
|
||
cond: str | None = PythonToStubConverter._CifConditionText(node.test)
|
||
if cond is None:
|
||
return ''
|
||
result: list[str] = [f'{IndentStr}cif {cond}:']
|
||
for item in node.body:
|
||
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
|
||
|
||
current: ast.If = node
|
||
while current.orelse:
|
||
if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If):
|
||
# celif 情况
|
||
elif_node: ast.If = current.orelse[0]
|
||
econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test)
|
||
if econd is None:
|
||
break
|
||
result.append(f'{IndentStr}celif {econd}:')
|
||
for item in elif_node.body:
|
||
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
|
||
current = elif_node
|
||
continue
|
||
# celse 情况
|
||
result.append(f'{IndentStr}celse:')
|
||
for item in current.orelse:
|
||
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
|
||
break
|
||
return '\n'.join(result)
|
||
|
||
@staticmethod
|
||
def _ProcessMacroBodyItem(item: ast.AST, indent: int = 0) -> list[str]:
|
||
"""处理宏体内的单个语句"""
|
||
IndentStr: str = ' ' * indent
|
||
result: list[str] = []
|
||
|
||
if isinstance(item, ast.ClassDef):
|
||
class_stub: list[str] = PythonToStubConverter._ConvertClass(item)
|
||
for line in class_stub:
|
||
if line.strip():
|
||
result.append(f'{IndentStr}{line}')
|
||
else:
|
||
result.append(line)
|
||
elif isinstance(item, ast.FunctionDef):
|
||
FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent)
|
||
result.extend(FuncStub)
|
||
elif isinstance(item, ast.AnnAssign):
|
||
TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation)
|
||
result.append(f'{IndentStr}{item.target.id}: {TypeStr}')
|
||
elif isinstance(item, ast.Expr):
|
||
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
|
||
if ExprStr:
|
||
result.append(f'{IndentStr}{ExprStr}')
|
||
elif isinstance(item, ast.If):
|
||
# 处理嵌套的 if 宏
|
||
NestedIf: str = PythonToStubConverter._ProcessIfMacro(item, indent=indent)
|
||
if NestedIf:
|
||
result.append(NestedIf)
|
||
|
||
return result |