Files
TransPyC/lib/core/Translator/AnnotationLoader.py
2026-07-18 19:25:40 +08:00

382 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import ast
import os
import importlib
import types
from typing import Any
import lib._bootstrap # noqa: F401 设置 sys.path项目根 + lib/includes
from lib.core.Handles.HandlesBase import CTypeInfo
from lib.core.SymbolUtils import IsTModule, AnnotationContainsName
from lib.includes import t
from lib.includes.t import CType, CEnum
from lib.constants.config import mode as _config_mode
from lib.core.VLogger import get_logger as _vlog
class AnnotationLoaderMixin:
"""注解模块加载 Mixin
提供注解模块/文件的加载与符号表注册能力。
"""
def _LoadAnnotationModule(self, ModuleName: str, library_name: str | None = None, parse_method: str = 'auto') -> int:
"""加载注解模块并将 CType 子类注册为 typedef
支持两种模式:
1. AST 模式:如果 ModuleName 是文件路径,解析文件提取 CType
2. importlib 模式:如果 ModuleName 是模块名,导入模块提取 CType
Args:
ModuleName: 模块名或文件路径
library_name: 手动指定的库名称,用于注册到符号表。如果为 None则从 ModuleName 推断
parse_method: 解析方法,可选值:
- 'auto': 自动检测(默认)
- 'file': 强制使用 AST 模式(文件路径)
- 'module': 强制使用 importlib 模式(模块名)
"""
def register_to_symboltable(module: types.ModuleType, lib_name: str | None = None, source_file: str | None = None) -> int:
"""将模块中的 CType 子类和 CEnum 类注册到符号表"""
count: int = 0
for AttrName in dir(module):
attr: type = getattr(module, AttrName)
# 优先检查是否是 CEnum 子类(枚举)
if isinstance(attr, type) and issubclass(attr, CEnum) and attr is not CEnum:
# 注册枚举类型
EnumNode: CTypeInfo = CTypeInfo()
EnumNode.Name = AttrName
EnumNode.BaseType = t.CEnum()
EnumNode.IsEnum = True
EnumNode.set('enum_class', attr)
EnumNode.set('source', 'annotation_module')
EnumNode.library_name = lib_name
EnumNode.file = source_file
self.SymbolTable.insert(AttrName, EnumNode)
if lib_name:
FullName: str = f'{lib_name}.{AttrName}'
full_EnumNode: CTypeInfo = CTypeInfo()
full_EnumNode.Name = FullName
full_EnumNode.IsEnum = True
full_EnumNode.set('enum_class', attr)
full_EnumNode.set('source', 'annotation_module')
full_EnumNode.library_name = lib_name
full_EnumNode.file = source_file
self.SymbolTable.insert(FullName, full_EnumNode)
# 枚举成员也需要注册
for MemberName in dir(attr):
if not MemberName.startswith('_'):
member: int = getattr(attr, MemberName, None)
if isinstance(member, int):
MemberNode: CTypeInfo = CTypeInfo()
MemberNode.Name = MemberName
MemberNode.BaseType = t.CEnum(AttrName)
MemberNode.value = member
MemberNode.EnumName = AttrName
MemberNode.library_name = lib_name
MemberNode.IsEnumMember = True
self.SymbolTable.insert(MemberName, MemberNode)
self.SymbolTable.insert(f"{AttrName}.{MemberName}", MemberNode)
self.SymbolTable.insert(f"{AttrName}_{MemberName}", MemberNode)
if lib_name:
self.SymbolTable.insert(f"{lib_name}.{AttrName}.{MemberName}", MemberNode)
self.SymbolTable.insert(f"{lib_name}_{AttrName}_{MemberName}", MemberNode)
count += 1
# 检查是否是 CType 子类typedef 别名)
elif isinstance(attr, type) and issubclass(attr, CType) and attr is not CType:
# 继承 CType 的类是类型别名typedef不是结构体
# 使用手动指定的库名称作为前缀
if lib_name:
FullName: str = f'{lib_name}.{AttrName}'
else:
FullName = AttrName
# 同时注册带前缀和不带前缀的名称(都是 typedef 别名)
TypedefNode: CTypeInfo = CTypeInfo()
TypedefNode.Name = AttrName
TypedefNode.IsTypedef = True
TypedefNode.OriginalType = attr.__name__
TypedefNode.set('source', 'annotation_module')
TypedefNode.library_name = lib_name
TypedefNode.file = source_file
self.SymbolTable.insert(AttrName, TypedefNode)
if lib_name and FullName != AttrName:
FullTypedef_node: CTypeInfo = CTypeInfo()
FullTypedef_node.Name = FullName
FullTypedef_node.IsTypedef = True
FullTypedef_node.OriginalType = attr.__name__
FullTypedef_node.set('source', 'annotation_module')
FullTypedef_node.library_name = lib_name
FullTypedef_node.file = source_file
self.SymbolTable.insert(FullName, FullTypedef_node)
count += 1
# 检查是否是下划线前缀的 CType 子类(如 _CTypedef -> CTypedef
elif AttrName.startswith('_') and len(AttrName) > 1:
PublicName: str = AttrName[1:]
PublicAttr: type | None = getattr(module, PublicName, None)
if PublicAttr is not None and not isinstance(attr, type):
continue
if isinstance(attr, type) and issubclass(attr, CType) and attr is not CType:
if not self.SymbolTable.has(PublicName):
if lib_name:
FullName: str = f'{lib_name}.{PublicName}'
else:
FullName = PublicName
TypedefNode: CTypeInfo = CTypeInfo()
TypedefNode.Name = PublicName
TypedefNode.IsTypedef = True
TypedefNode.OriginalType = attr.__name__
TypedefNode.set('source', 'annotation_module')
TypedefNode.library_name = lib_name
TypedefNode.file = source_file
self.SymbolTable.insert(PublicName, TypedefNode)
if lib_name and FullName != PublicName:
FullTypedef_node: CTypeInfo = CTypeInfo()
FullTypedef_node.Name = FullName
FullTypedef_node.IsTypedef = True
FullTypedef_node.OriginalType = attr.__name__
FullTypedef_node.set('source', 'annotation_module')
FullTypedef_node.library_name = lib_name
FullTypedef_node.file = source_file
self.SymbolTable.insert(FullName, FullTypedef_node)
count += 1
return count
# 确定解析方法
use_file_mode: bool = False
if parse_method == 'file':
use_file_mode = True
elif parse_method == 'module':
use_file_mode = False
else: # auto
use_file_mode = os.path.isfile(ModuleName)
# 确定库名称
lib_name: str | None = library_name
if lib_name is None:
if use_file_mode:
# 从文件路径推断库名称
lib_name = os.path.splitext(os.path.basename(ModuleName))[0]
else:
# 模块名就是库名称
lib_name = ModuleName
# 根据解析方法加载模块
if use_file_mode:
# AST 模式:使用 AST 解析文件,不执行代码
try:
# 临时保存当前的 EmbeddedAssignments 和 TypedefAssignments
saved_embedded: dict = self.EmbeddedAssignments.copy()
saved_typedef: dict = self.TypedefAssignments.copy()
# 清空 PD 收集(注解文件不应该收集 PD 语句)
self.EmbeddedAssignments = {}
self.TypedefAssignments = {}
# 直接调用 ParsePythonFile 解析文件提取类型信息
# 注意:这不会执行任何 Python 代码,只是解析 AST
# UpdateCurrentFile=False 表示不修改 CurrentFile
self.ParsePythonFile(ModuleName, UpdateCurrentFile=False)
# 恢复主代码文件的 PD 收集
self.EmbeddedAssignments = saved_embedded
self.TypedefAssignments = saved_typedef
# 从文件名推断类型前缀(如 test_t -> test_t.xxx
TypePrefix: str = lib_name
count: int = 0
# 检查符号表中新增的类型(注解文件解析后的所有 struct 类型)
# 需要检查这些类型是否继承自 CType如果是则是 typedef 别名
new_types: list[str] = []
for TypeName, TypeInfo in self.SymbolTable.items():
if TypeInfo.IsStruct:
new_types.append(TypeName)
# 再次遍历 AST检测继承自 CType 的类
try:
with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read()
ann_tree: ast.Module = ast.parse(ann_content)
for node in ann_tree.body:
if isinstance(node, ast.ClassDef):
ClassName: str = node.name
# 检查是否有基类
for base in node.bases:
if isinstance(base, ast.Name) and base.id == 'CType':
# 这个类继承自 CType是 typedef 别名
# 更新符号表中的类型为 typedef
if self.SymbolTable.has(ClassName):
TypedefNode: CTypeInfo = CTypeInfo()
TypedefNode.Name = ClassName
TypedefNode.IsTypedef = True
TypedefNode.OriginalType = self.SymbolTable[ClassName].get('OriginalType', f'struct {ClassName}')
TypedefNode.set('source', 'annotation_module')
TypedefNode.set('is_ctype_subclass', True)
self.SymbolTable.insert(ClassName, TypedefNode)
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"解析注解文件类型定义失败: {_e}", "Exception")
# 为所有新增的类型添加库前缀和 source 标记
for TypeName in new_types:
# 添加带前缀的类型名
FullName: str = f'{TypePrefix}.{TypeName}'
TypeInfo: CTypeInfo | dict[str, Any] = self.SymbolTable[TypeName].copy()
TypeInfo['source'] = 'annotation_module'
TypeInfo['original_name'] = TypeName
self.SymbolTable.insert(FullName, TypeInfo)
count += 1
# 再次遍历 AST查找注解文件中的 typedef 语句AnnAssign with t.CTypedef
# 注意:必须在类定义处理完之后再处理 typedef
typedef_annots: list[tuple[str, str]] = []
# 查找 CEnum 成员变量AnnAssign with t.CEnum | t.State 或 t.CEnum
cEnumMembers: list[tuple[str, int | None]] = []
try:
with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read()
ann_tree: ast.Module = ast.parse(ann_content)
for node in ann_tree.body:
# 检测 xxx: t.CEnum 形式的枚举成员变量
if isinstance(node, ast.AnnAssign):
if isinstance(node.target, ast.Name) and node.annotation:
TargetName: str = node.target.id
# 检查注解是否是 BinOp (Union) 或直接是 Attribute
IsCenum: bool = False
if isinstance(node.annotation, ast.BinOp):
# 检查是否包含 CEnum
if AnnotationContainsName(node.annotation, 'CEnum'):
IsCenum = True
elif isinstance(node.annotation, ast.Attribute):
# 直接是 t.CEnum
if node.annotation.attr == t.CEnum.__name__:
IsCenum = True
if IsCenum:
# 检查值是否是 Constant (枚举成员值)
if node.value and isinstance(node.value, ast.Constant):
value: int | str | float | None = node.value.value
else:
value = None
cEnumMembers.append((TargetName, value))
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"解析 CEnum 成员变量失败: {_e}", "Exception")
# 处理 CEnum 成员变量
for TargetName, value in cEnumMembers:
# 注册枚举成员
MemberNode: CTypeInfo = CTypeInfo()
MemberNode.Name = TargetName
MemberNode.BaseType = t.CEnum(TypePrefix)
MemberNode.value = value
MemberNode.EnumName = TypePrefix
MemberNode.library_name = TypePrefix
MemberNode.IsEnumMember = True
self.SymbolTable.insert(TargetName, MemberNode)
self.SymbolTable.insert(f"{TypePrefix}.{TargetName}", MemberNode)
self.SymbolTable.insert(f"{TypePrefix}_{TargetName}", MemberNode)
try:
with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read()
ann_tree: ast.Module = ast.parse(ann_content)
for node in ann_tree.body:
# 检测 xxx: t.CTypedef = xxx 形式的 typedef 语句
if isinstance(node, ast.AnnAssign):
if isinstance(node.target, ast.Name) and node.annotation and node.value:
TargetName: str = node.target.id
# 检查注解是否是 t.CTypedef
if isinstance(node.annotation, ast.Attribute):
if isinstance(node.annotation.value, ast.Name):
if IsTModule(node.annotation.value.id) and node.annotation.attr == 'CTypedef':
# 检查值是否是 Name
if isinstance(node.value, ast.Name):
original_name: str = node.value.id
typedef_annots.append((TargetName, original_name))
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"解析 typedef 语句失败: {_e}", "Exception")
# 处理 typedef 注解
for TargetName, original_name in typedef_annots:
# 检查原始类型是否在符号表中(带前缀或不带前缀)
orig_with_prefix: str = f'{TypePrefix}.{original_name}'
if self.SymbolTable.has(orig_with_prefix):
# 添加带前缀的 typedef 别名
FullTypedef_name: str = f'{TypePrefix}.{TargetName}'
TypedefNode: CTypeInfo = CTypeInfo()
TypedefNode.Name = FullTypedef_name
TypedefNode.IsTypedef = True
orig_entry: CTypeInfo | dict[str, Any] = self.SymbolTable.lookup(orig_with_prefix)
orig_type_str: str = f'struct {original_name}'
if isinstance(orig_entry, dict):
if orig_entry.get('type') == 'typedef':
orig_type_str = orig_entry.get('OriginalType', f'struct {original_name}')
elif orig_entry.get('type') == 'enum':
orig_type_str = f'enum {original_name}'
elif orig_entry.IsTypedef:
orig_type_str = orig_entry.OriginalType or f'struct {original_name}'
elif orig_entry.IsEnum:
orig_type_str = f'enum {original_name}'
TypedefNode.OriginalType = orig_type_str
TypedefNode.set('source', 'annotation_module')
TypedefNode.set('original_name', TargetName)
self.SymbolTable.insert(FullTypedef_name, TypedefNode)
count += 1
self.AnnotationModules.add(lib_name)
return count
except Exception as e:
# 恢复 PD 收集
self.EmbeddedAssignments = saved_embedded
self.TypedefAssignments = saved_typedef
self.LogWarning(f"AST模式加载注解模块失败: {ModuleName}, 错误: {e}")
return 0
else:
# importlib 模式:导入模块
try:
module: types.ModuleType = importlib.import_module(ModuleName)
count: int = register_to_symboltable(module, lib_name, None)
self.AnnotationModules.add(lib_name)
return count
except ImportError:
self.LogWarning(f"importlib模式加载注解模块失败: {ModuleName}")
return 0
except Exception as e:
self.LogWarning(f"加载注解模块失败: {ModuleName}, 错误: {e}")
return 0
def _LoadAnnotationFiles(self, FilePaths: list[str | tuple]) -> None:
"""加载多个注解文件并注册到符号表
Args:
FilePaths: 文件路径列表,每个元素可以是:
- str: 文件路径或模块名
- tuple: (path, options) 或 (path, library_name, parse_method)
"""
for item in FilePaths:
if isinstance(item, tuple):
if len(item) >= 3:
path: str
library_name: str
parse_method: str
path, library_name, parse_method = item[:3]
count: int = self._LoadAnnotationModule(path, library_name, parse_method)
elif len(item) == 2:
path: str
library_name: str
path, library_name = item
count: int = self._LoadAnnotationModule(path, library_name, 'auto')
else:
count: int = self._LoadAnnotationModule(item)
else:
count: int = self._LoadAnnotationModule(item)
# 别名,保持向后兼容
LoadAnnotationFiles = _LoadAnnotationFiles