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